简体   繁体   中英

What purpose do generic constructors serve in Java?

As everyone knows you can have a generic class in Java by using type arguments:

class Foo<T> {
    T tee;
    Foo(T tee) {
        this.tee = tee;
    }
}

But you can also have generic constructors , meaning constructors that explicitly receive their own generic type arguments, for example:

class Bar {
    <U> Bar(U you) {
        // Why!?
    }
}

I'm struggling to understand the use case. What does this feature let me do?

The use case I'm thinking of might be that some wants an Object which inherits from 2 Types. Eg implements 2 interfaces :

public class Foo {

    public <T extends Bar & Baz> Foo(T barAndBaz){
        barAndBaz.barMethod();
        barAndBaz.bazMethod();
    }
}

Though I have never used it in production.

It's clear in the example you provided that U does not play any role in the class' constructor, as it effectively becomes an Object at runtime:

class Bar {
    <U> Bar(U you) {
        // Why!?
    }
}

But let's say that I wanted my constructor to only accept types that extend some other class or interface, as seen below:

class Foo<T extends Baz> {
    <U extends Bar> Foo(U u) {
        // I must be a Bar!
    }
}

Notice that the class already has a different generic type in use; this allows you to utilize a separate, unrelated generic type to the class definition.

Granted, I've never used something like this, and I've never seen it in use, but it's possible!

What does this feature let me do?

There are at least three two things it lets you do that you could not otherwise do:

  1. express relationships between the types of the arguments, for example:

     class Bar { <T> Bar(T object, Class<T> type) { // 'type' must represent a class to which 'object' is assignable, // albeit not necessarily 'object''s exact class. // ... } } 
  2. <withdrawn>

  3. As @Lino observed first, it lets you express that arguments must be compatible with a combination of two or more unrelated types (which can make sense when all but at most one are interface types). See Lino's answer for an example.

Actually, this constructor

class Bar {
    <U> Bar(U you) {
        // Why!?
    }
}

is just like a generic method . It would make a lot more sense if you had multiple constructor arguments like this:

class Bar {
    <U> Bar(U you, List<U> me) {
        // Why!?
    }
} 

Then you could enforce the constraint, that they have the same time with the compiler. Without making U a generic for the whole class.

Because that unbound generic type erases to Object , it'd be the same as passing Object in your constructor:

public class Foo {
    Object o;

    public Foo(Object o) {
        this.o = o;
    }
}

...but like passing a blank Object , unless you're doing something clever , this has little practical value.

You see benefits and wins if you pass in bound generics instead, meaning that you can actually have guarantees around the type that you care about.

public class Foo<T extends Collection<?>> {
    T collection;
    public Foo(T collection) {
        this.collection = collection;
    }
}

Effectively, this is more about flexibility than it being something revolutionary. If you want the flexibility to pass in a specific category of types, then you have the power to do so here. If you don't , then there's nothing wrong with standard classes. It's merely here for your convenience, and since type erasure is still a thing, an unbound generic is the same (and has the same utility) as passing Object .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM