Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java generics and specialized constructors

(This is probably a duplicate, but I could not find it - feel free to point it out)

Consider the following Java class:

public class A<T0, T1> {
    public A(T0 t0, T1 t1) {
        ...
    }
}

Instantiating this class is easy using something along the lines of new A<Integer, String>(1, "X").

Suppose now that most instances of this class have a String as the second type parameter T1 and that the object of this type used in the constructor call is also pretty much standard.

If A had not been using generics, a common extension would be an additional constructor without the second argument:

public class A {
    public A(int t0, String t1) {
        ...
    }

    public A(int t0) {
        this(t0, new String("X"));
    }
}

Unfortunately, this does not seem to be possible for a class that does use generics - at least not without a forced cast:

    public A(T0 t0) {
        this(t0, (T1)(...));
    }

The reason? While this constructor only takes a single argument, it still uses two type parameters and there is no way to know a priori that whatever type T1 the user of the class supplies will be compatible with the default value used in the constructor.

A slightly more elegant solution involves the use of a subclass:

 public class B<T0> extends A<T0, String> {
     ...
 }

But this approach forces yet another branch in the class hierarchy and yet another class file with what is essentially boilerplate code.

  • Is there a way to declare a constructor that forces one or more of the type parameters to a specific type? Something with the same effects as using a subclass, but without the hassle?

  • Is there something fundamentally wrong in my understanding of generics and/or my design? Or is this a valid issue?

like image 245
thkala Avatar asked Dec 12 '11 17:12

thkala


2 Answers

Easiest method is just to add a static creation method.

public static <T0> A<T0,String> newThing(T0 t0) {
    return new A<T0,String>(t0, "X");
}

(Perhaps choose a name appropriate for the particular usage. Usually no need for new String("...").)

From Java SE 7, you can use the diamond:

A<Thing,String> a = new A<>(thing);
like image 99
Tom Hawtin - tackline Avatar answered Oct 07 '22 06:10

Tom Hawtin - tackline


As I understand it, you want to have a second constructor that (if called) would force the generic type T1 to be a String.

However, the generics are specified BEFORE you call the constructor.

That second constructor, if valid, could allow someone to do this:

B<Integer, Integer> b = new B<Integer, Integer>(5);

The error here is that you've specified the second generic type as an Integer BEFORE calling the constructor. And then the constructor would, in theory, specify the second generic type as a String. Which is why I believe it's not allowed.

like image 30
Jeff Goldberg Avatar answered Oct 07 '22 06:10

Jeff Goldberg