2

Problem

I'm trying to use Java generics to replace classes with similar methods. All of the examples that I've found cover simple examples, but I'm not sure if Java Generics are intended to be used this way.

I have 2 parent classes, and 2 child classes that have almost identical methods. Both parent classes derive from different classes as well. Ultimately, I want to be able to use one block of code to create and manipulate one of the parent classes, then its child class without a lot of switch statements or other flow control with duplicate code.

This is what I had in mind, even though I haven't beeen able to get it to work this way yet, whether it be syntax, or just not a feature of Generics.

Parent Classes

public class FooParent
{
    private FooChild fooChild;
    public FooChild getChild()
    {
        return fooChild;
    }
}

public class BarParent
{
    private BarChild barChild;
    public BarChild getChild()
    {
        return barChild;
    }
}

Child Classes

public class FooChild
{
    public void print()
    {
        System.out.println("I'm a foo child");
    }
}

public class BarChild
{
    public void print()
    {
        System.out.println("I'm a bar child");
    }
}

Generic Classes

public class GenericParent<T>
{
    private T self;
    public GenericParent(T self)
    {
        this.self = self;
    }

    public GenericChild getChild()
    {
        return new GenericChild(self.getChild());
    }
}

public class GenericChild<T>
{
    private T self;
    public GenericChild(T self)
    {
        this.self = self;
    }

    public void print()
    {
        self.print();
    }
}

How I want to use them

public static void main(String args[])
{
    GenericParent parent;

    // Only the initialization of the parent variable needs specialized code
    switch(args[0])
    {
        case "foo":
            parent = new GenericParent(new FooParent());
        break;

        case "bar":
            parent = new GenericParent(new BarParent());
        break;
    }

    // From here on out, it's all generic
    parent.getChild().print();
}

Usage and desired output

java genericExample foo
> I'm a foo child

java genericExample bar
> I'm a bar child

Final Questions

Maybe "child" and "parent" are misnomers, because I know they're not actually inherited, but the bottom line is, the one class returns its "child" with certain methods. So this is a lot of code for a problem that may not actually be solvable this way, but hopefully you can answer me this:

  1. Is this something that Java Generics can accomplish?
  2. If not, is there a solution to this problem in Java?

Thanks!

Edit

My "Foo" and "Bar" classes are uneditable by me. My ultimate question is: can I store one instance of either class in a single variable without using a common parent class?

Taylor Lopez
  • 813
  • 1
  • 8
  • 28
  • have you considered using reflection to fire .getChild() on the parents and .print() on the children? – Andreas May 27 '14 at 17:31
  • 1
    Another, perhaps cleaner, option is to have the parents and children adhere to an interface which exposes the methods you need in main. – Andreas May 27 '14 at 17:33
  • What's your **actual** problem? Foo and Bar don't really tell whether there's a nice solution or if you're just attempting to abuse generics. – Kayaman May 27 '14 at 17:36
  • what do you want to implement actually? maybe OOP is not the best fit for your problem? – Display Name May 27 '14 at 17:43
  • The "actual problem" is that I have two classes like Foo and Bar. Both classes are necessary. I did not write these classes, and I can only use them as they are. After initialization, using an instance of either class is the same, so all I need is a way to store either class instance into a single variable that can be used so I don't need a bunch of switch statements and duplicate code. – Taylor Lopez May 27 '14 at 17:45
  • @Andreas, the reflection Idea may be the one for this problem. Since I can't edit the parent classes and make them implement any interfaces, this may be the best solution. Thanks! – Taylor Lopez May 27 '14 at 18:56
  • @Mortos, If there are a lot of potential foo/bar type classes that you expect to have to call `.getChiled().print()` on, you might want to consider some sort of wrapper factory: a class which, given a non-specific parent, will return an anonymous class with the `.print()` method you are looking for. – Andreas May 27 '14 at 19:23

4 Answers4

3

I think you want polymorphism, not generics:

public class test {
    public class FooParent implements hasPrintableChildren
    {
        private FooChild fooChild;
        public FooChild getChild()
        {
            return fooChild;
        }
    }

    public class BarParent implements hasPrintableChildren
    {
        private BarChild barChild;
        public BarChild getChild()
        {
            return barChild;
        }
    }


    public class FooChild implements canPrint
    {
        public void print()
        {
            System.out.println("I'm a foo child");
        }
    }

    public class BarChild implements canPrint
    {
        public void print()
        {
            System.out.println("I'm a bar child");
        }
    }

    public interface hasPrintableChildren{
        public canPrint getChild();
    }
    public interface canPrint{
        public void print();
    }



    public static void main(String args[])
    {
        hasPrintableChildren parent;
        // Only the initialization of the parent variable needs specialized code
        switch(args[0])
        {
            case "foo":
                parent = new FooParent();
            break;

            case "bar":
                parent = new BarParent();
            break;
        }

        // From here on out, it's all generic
        parent.getChild().print();
    }
}

OP clarified that he would be interested in the reflection option:

    public static void main(String args[]) throws IllegalArgumentException, SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException
    {
        Object parent;
        // Only the initialization of the parent variable needs specialized code
        switch(args[0])
        {
            case "foo":
                parent = new FooParent();
            break;

            case "bar":
                parent = new BarParent();
            break;
        }

        // From here on out, it's all generic
        Object child = parent.getClass().getMethod("getChild").invoke(parent);
        child.getClass().getMethod("print").invoke(child);
    }

Note: I would not recommend this sort of hard coded reflection. Code like this generally stinks of a bigger design problem.

Andreas
  • 4,937
  • 2
  • 25
  • 35
0

Your parent seems a wrapper, a wrapper is a container, so yes it may be something that can benefit from a type parameter.

But I can't see any type parameter except in the constructor signature (and what is self? No bounds, no hints, no anything...), so using a generic type doesn't buy anything to you here. It's no use to introduce a type parameter if the methods you are interested in return void and declare an empty parameter list.

Here's the guidance: if methods in your classes would benefit from having a type parameter, ie if a type parameter is useful in any method return type or in the signature, then genericize your class. Otherwise, stick with what you currently have.

Raffaele
  • 20,627
  • 6
  • 47
  • 86
0

No. This is not usually something you would use Generics for, this is something you would use an Interface or an Abstract class for, and in your example, probably anonymous inner classes.

Here is an example that pretty much shows all that:

Main.java

public class Main {
    public static void main(String args[])
    {
        AbstractParent parent;
        // Only the initialization of the parent variable needs specialized code
        switch(args[0])
        {
            case "foo":
                parent = new FooParent();
                break;

            default:
                parent = new BarParent();
                break;
        }

        // From here on out, it's all generic
        parent.getChild().print();
    }
}

Child.java

public interface Child {
    void print();
}

AbstractParent.java

public abstract class AbstractParent {
    protected Child child;

    public Child getChild() {
        return child;
    }
}

BarParent.java

public class BarParent extends AbstractParent {
    public BarParent() {
        child = new Child() {
            @Override
            public void print() {
                System.out.println("I'm a bar child");
            }
        };
    }
}

FooParent.java

public class FooParent extends AbstractParent {
    public FooParent() {
        child = new Child() {
            @Override
            public void print() {
                System.out.println("I'm a foo child");
            }
        };
    }
}

With some of the new language features in Java 8, you can do even cooler things. But let's leave that for another time.

Mikkel Løkke
  • 3,710
  • 23
  • 37
0

Yes, generics together with polymorphism can help you:

public class Foo {} // declared elsewhere


public class Bar {} // declared elsewhere


public abstract class GenericParent<T> {
    private T self;

    public GenericParent(T self) {
        this.self = self;
    }

    protected T getSelf() {
        return self;
    }

    public abstract GenericChild<T> getChild();
}


public class FooChild extends GenericChild<Foo> {
    public FooChild(Foo foo) {
        super(foo);
    }
}


public class BarChild extends GenericChild<Bar> {
    public BarChild(Bar bar) {
        super(bar);
    }
}


public class FooParent extends GenericParent<Foo> {

    public FooParent(Foo foo) {
        super(foo);
    }

    public FooParent() {
        this(new Foo()); 
    }

    @Override
    public GenericChild<Foo> getChild() {
        return new FooChild(getSelf());
    }
}


public class BarParent extends GenericParent<Bar> {

    public BarParent(Bar bar) {
        super(bar);
    }

    public BarParent() {
        this(new Bar());
    }

    @Override
    public GenericChild<Bar> getChild() {
        return new BarChild(getSelf());
    }
}

You also have to change your main method slightly:

public static void main(String args[]) {
    GenericParent<?> parent;

    // Only the initialization of the parent variable needs specialized code
    switch(args[0]) {
        case "foo":
            parent = new FooParent();
            break;

        case "bar":
            parent = new BarParent();
            break;
    }

    parent.getChild().print();
}
matsev
  • 32,104
  • 16
  • 121
  • 156
  • I appreciate the answer. I see how it would make sense that way. Unfortunately, I cannot modify the "Foo" and "Bar" classes that I have. I'm stuck with them the way they are. – Taylor Lopez May 27 '14 at 18:17