泛型构造函数在Java中的作用是什么?

泛型构造函数在Java中的作用是什么?,java,generics,constructor,Java,Generics,Constructor,众所周知,您可以使用类型参数在Java中创建泛型类: class Foo<T> { T tee; Foo(T tee) { this.tee = tee; } } class-Foo{ T形三通; 傅(T-tee){ this.tee=tee; } } 但也可以有泛型构造函数,这意味着构造函数显式地接收自己的泛型类型参数,例如: class Bar { <U> Bar(U you) { // Why!?

众所周知,您可以使用类型参数在Java中创建泛型类:

class Foo<T> {
    T tee;
    Foo(T tee) {
        this.tee = tee;
    }
}
class-Foo{
T形三通;
傅(T-tee){
this.tee=tee;
}
}
但也可以有泛型构造函数,这意味着构造函数显式地接收自己的泛型类型参数,例如:

class Bar {
    <U> Bar(U you) {
        // Why!?
    }
}
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.
        // ...
    }
}
类栏{
酒吧(你){
//为什么!?
}
}

我正在努力理解这个用例。此功能允许我做什么?

因为未绑定的泛型类型将擦除到
对象
,这与在构造函数中传递
对象
相同:

public class Foo {
    Object o;

    public Foo(Object o) {
        this.o = o;
    }
}
…但就像传递一个空白的
对象
,除非你做了一些聪明的事情,否则这没有什么实用价值

如果您传入绑定泛型,您将看到好处和收益,这意味着您实际上可以对所关心的类型进行保证

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

public class Foo在您提供的示例中很明显,
U
在类的构造函数中不起任何作用,因为它在运行时实际上变成了一个
对象

class Bar {
    <U> Bar(U you) {
        // Why!?
    }
}
类栏{
酒吧(你){
//为什么!?
}
}
但假设我希望我的构造函数只接受扩展其他类或接口的类型,如下所示:

class Foo<T extends Baz> {
    <U extends Bar> Foo(U u) {
        // I must be a Bar!
    }
}
class-Foo{
傅(U){
//我一定是酒吧!
}
}
请注意,该类已使用不同的泛型类型;这允许您使用与类定义无关的单独泛型类型


当然,我从来没有使用过这样的东西,也从来没有见过它在使用,但这是可能的

实际上,这个构造函数

class Bar {
    <U> Bar(U you) {
        // Why!?
    }
}
类栏{
酒吧(你){
//为什么!?
}
}
就像是一只猫。如果您有多个构造函数参数(如下所示),则更有意义:

class Bar {
    <U> Bar(U you, List<U> me) {
        // Why!?
    }
} 
类栏{
酒吧(你,把我列出来){
//为什么!?
}
} 

然后您可以强制执行约束,即它们与编译器具有相同的时间。没有使U成为整个类的泛型。

我想到的用例可能是一些人想要一个从两种类型继承的对象。例如,实现2个
接口

public class Foo {

    public <T extends Bar & Baz> Foo(T barAndBaz){
        barAndBaz.barMethod();
        barAndBaz.bazMethod();
    }
}
公共类Foo{
公共食品(T barAndBaz){
barAndBaz.barMethod();
barAndBaz.bazMethod();
}
}
虽然我从未在生产中使用过它

这个功能让我做什么

至少有三件两件事是它能让你做的,这是你无法做的:

  • 表示参数类型之间的关系,例如:

    class Bar {
        <U> Bar(U you) {
            // Why!?
        }
    }
    
    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.
            // ...
        }
    }
    
    类栏{
    条形图(T对象,类类型){
    //“type”必须表示“object”可赋值的类,
    //尽管不一定是“对象”的确切类别。
    // ...
    }
    }
    
  • 正如@Lino首先观察到的,它允许您表示参数必须与两个或多个不相关类型的组合兼容(当除最多一个之外的所有类型都是接口类型时,这是有意义的)。有关示例,请参见Lino的答案


  • 可能在这样的情况下:
    Bar(U a,U b)
    ?这个例子没有太大帮助。但泛型可以帮助您在必须扩展另一种类型或是交叉类型时,根据参数建立契约。(例如)通用方法很有用。构造函数与方法类似。即使没有特别引人注目的用例,禁止泛型构造函数也比允许泛型构造函数更难。提示:如果你能做某件事,并不意味着你应该做。请参阅。您可以要求多个参数应用于一致类型。编译器可以决定将对象作为U插入以解决此问题,在这种情况下,您仍然可以传递int和string对象。但请注意,除非在调用示例构造函数时显式指定类型参数,Java可以推断
    U
    Object
    ,因此没有任何限制。我知道这就像一个泛型方法,实际上它是一个具有特殊名称的泛型方法,
    。但是对于泛型方法,实际上通常处理的是
    静态
    方法,因为对于main,实例方法通常可以使用它们从类“继承”的“泛型性”来完成它们需要的工作。我想知道你能用泛型构造函数做些什么,因为构造函数主要初始化类状态,很难想象你根据类不可用的泛型维度设置类状态的场景…@keuleJ,我想你误解了@JohnBollinger的意思。关键是
    javac
    您的示例在编译时和运行时都与
    Bar(objectyou,objectme)
    完全相同。因此,泛型参数是冗余的,并且有点容易引起混淆。然而,泛型参数有很多用途,它们实际上表示有意义的约束,可以在编译时进行静态验证。如果您需要知道的是
    u
    Bar
    ,那么简单地执行
    Foo(Bar u){}
    不是更好的做法吗?我完全同意你的想法,即对
    Baz
    的后代进行泛型-一个很好的例子是
    用于基于比较的有序数据结构,例如搜索树
    Foo(U)
    ,其中Bar和Baz不相关(例如两个接口,这两个接口都是构造函数所要求的)。是的,如果
    Bar
    Baz
    在您的示例中是接口,您还会使用
    extends
    而不是
    implements
    ?@musicman523是的,因为
    implements
    不是合适的关键字,OP似乎专注于类
    Bar
    中的泛型构造函数,将泛型类
    Foo
    主要用于对比。虽然这个答案中的所有内容似乎都是正确的,但我有tr