开发者

generics calling constructor

开发者 https://www.devze.com 2022-12-10 12:00 出处:网络
I am trying to do something I would not normally do, it is a bit odd, but I\'d like to make it work.Essentially I have a factory that has to create objects by calling the constructor with different ty

I am trying to do something I would not normally do, it is a bit odd, but I'd like to make it work. Essentially I have a factory that has to create objects by calling the constructor with different types of 开发者_StackOverflow社区data (A and B take different types in the code below). I seem to have gotten my self stuck going down the generics route (I do need the code to be as compile time typesafe as possible). I am not opposed to writing the code differently (I'd like to keep the idea of the factory if possible, and I do not want to have to add in casts - so the "data" parameter cannot be an "Object").

Any thoughts on how to fix the code with generics or an alternative way of doing it that meets my requirements?

(Technically this is homework, but I am the instructor trying out something new... so it isn't really homework :-)

public class Main2
{
    public static void main(String[] args) 
    {
        X<?> x;

        x = XFactory.makeX(0, "Hello");
        x.foo();

        x = XFactory.makeX(1, Integer.valueOf(42));
        x.foo();
    }

}

class XFactory
{
    public static <T> X<T> makeX(final int i,
                                 final T   data)
    {
        final X<T> x;

        if(i == 0)
        {
            // compiler error: cannot find symbol constructor A(T)
            x = new A(data);
        }
        else
        {
            // compiler error: cannot find symbol constructor B(T)
            x = new B(data);
        }

        return (x);
    }
}

interface X<T>
{
    void foo();
}

class A
    implements X<String>
{
    A(final String s)
    {
    }

    public void foo()
    {
        System.out.println("A.foo");
    }
}

class B
    implements X<Integer>
{
    B(final Integer i)
    {
    }

    public void foo()
    {
        System.out.println("B.foo");
    }
}


I don't see a way to make it work. I don't really think it should work either. When calling your makeX() function the calling code needs to know what integer parameter corresponds to what type of data to pass in. IOW, your abstraction is very leaky in the first place, and what you're really implementing is a rudimentary form of polymorphism, which you might as well use method overloading for, i.e.:

X makeX(String data) {
  return new A(data);
}

X makeX(Integer data) {
  return new B(data);
}

Of course it's a toy problem and all that. One way to make it work would be to make the client aware of implementation classes and add a Class<T> argument that you instantiate through reflection. But I suppose that would be kind of defeating the purpose.


I don't think what you're trying to do is possible without casting.

With casting, you have two options

if(i == 0)
        {
            x = new A((Integer)data);
        }
        else
        {
            x = new B((String)data);
     }
}

or

class A
    implements X<String>
{
    A(final Object s)
    {
    }
}

...

class B
    implements X<Integer>
{
    B(final Object i)
    {
    }
}


Probably the closest thing you could get whilst retaining static type safety and having lazy construction is:

public static void main(String[] args) {
    X<?> x;

    x = aFactory("Hello").makeX();
    x.foo();

    x = bFactory(42).makeX();
    x.foo();
}

private static XFactory aFactory(final String value) {
    return new XFactory() { public X<?> makeX() {
        return new A(value);
    }};
}

public static XFactory bFactory(final Integer value) {
    return new XFactory() { public X<?> makeX() {
        return new B(value);
    }};
}

interface XFactory() {
     X<?> makeX();
}

So we create an instance of an abstract factory that creates the appropriate instance with the appropriate argument. As a factory, the product is only constructed on demand.

Clearly something had to give. What would you expect XFactory.makeX(1, "Hello") to do?


This is not possible without casting. As I have said elsewhere - generics don't remove the need for casting, but they mean that you can do all the casting in one place.

In the setup you describe, the factory method is exactly where all the under-the-hood work takes place. It's the spot where your code tells the compiler "I know you don't know what these types are, but I do, so relax.

It's entirely legit for your factory method to know that if i==1, then the data must be be of type Integer, and to check/enforce this with casting.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号