Java中的通用构造函数有什么作用?

36
众所周知,在Java中,您可以通过使用类型参数来创建通用类:
class Foo<T> {
    T tee;
    Foo(T tee) {
        this.tee = tee;
    }
}

但你也可以拥有通用的构造函数,即显式接收自己的通用类型参数的构造函数,例如:

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

我很难理解使用案例。这个功能让我能做什么?

11
也许在像这样的情况下:<U> Bar(U a, U b) - Henry
1
这个例子并不是很有帮助。但是泛型可以帮助您在需要扩展另一种类型或成为交集类型的参数上建立契约。(例如:<U extends Serializable>) - M. Reif
7
通用方法很有用。构造函数类似于方法。禁止泛型构造函数比允许它们更费力,即使没有特别强制的使用情况。 - Sebastian Redl
5
提示:如果你“可以”做某事并不意味着你“应该”去做。 - Alma Do
请参见 https://dev59.com/I2Ml5IYBdhLWcg3wuY_K#18176765,你的问题几乎是一个重复的问题,泛型构造函数和不返回任何内容的泛型方法之间没有区别。 - Oleg
2
请参阅javafx.animation.KeyValue。您可以要求多个参数应用于一致的类型。 - VGR
5个回答

24

我正在考虑的使用案例可能是有人想要一个继承自2个类型的对象。例如,实现了2个接口:

public class Foo {

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

虽然我从未在生产环境中使用过它。


1
这是一个很好的例子。我没想到!严格来说,你也可以用 interface BarBaz extends Bar, Baz 或类似的方式解决这个问题,但这可能是一种更好的表达方式。但有趣的是,它更加“弱类型”,因为没有运行时类型。 - 0xbe5077ed
@0xbe5077ed 没错,由于运行时类型未知,或者根本不需要,因此这更具有动态性。 - Lino

19

从您提供的示例中可以明显看出,在类的构造函数中,U并没有扮演任何角色,因为在运行时它实际上变成了一个Object

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

假设我想让构造函数只接受继承自其他类或接口的类型,如下所示:

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

请注意,该类已经使用了不同的泛型类型;这使您可以利用与类定义不相关的单独的泛型类型。
尽管我从未使用过这样的东西,也从未见过它的使用,但这是可能的!

21
如果你只需要知道u是一个Bar,那么简单地使用Foo(Bar u) {}不是更好的做法吗?我完全赞同你对于泛型化Baz后代的想法 - 一个很好的例子是针对基于比较排序的数据结构,如搜索树的 <T extends Comparable> - musicman523
6
<U extends Bar & Baz> Foo(U u) 表示泛型方法 Foo 接受一个类型参数 U,该类型参数必须同时实现 BarBaz 两个接口(即 U 必须是 BarBaz 的交集)。注意,BarBaz 之间并没有继承关系(例如它们是两个接口),但在这个方法中都被要求。 - Draco18s no longer trusts SE
是的,那样会更好。 - Jacob G.
@Draco18s 如果在您的示例上下文中 BarBaz 是接口,您仍然会使用 extends 而不是 implements 吗? - musicman523
@musicman523 是的,因为implements不是适当的关键字,所以无法编译。 - Jacob G.
显示剩余2条评论

15

这个功能让我可以做什么?

至少有两件事情是这个功能可以让你做到,否则你是无法完成的:

  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.


1
第二个示例不会将无关类型的 T 推断为 Object,从而让任何东西通过吗? - user2357112
@user2357112,如果您依赖类型推断并且没有其他约束条件,那么在情况2中可以推断出Object类型。当存在其他约束条件时,设置下限更加有趣。 - John Bollinger
第二种情况无法编译,您不能以这种方式使用下限。 - Oleg
好的,@Oleg,第二项已撤回。 - John Bollinger
@JohnBollinger,我认为你在#1方面做得很好。我想到集合可能是自然的用例,但#1同样有效。还请参阅我在此线程中其他地方的答案。 - 0xbe5077ed

10

实际上,这个构造函数

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

就像泛型方法一样。如果您有多个构造函数参数,那么这将更加有意义:

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

那么您可以强制执行约束条件,使其与编译器具有相同的时间,而不将U作为整个类的通用类型。


4
编译器可以决定将Object插入到U中以解决问题,在这种情况下,您仍然可以传递一个int和一个字符串对象。 - Ferrybig
4
请注意,除非你在调用示例构造函数时显式指定类型参数,否则Java可以推断出UObject,从而不会加限制。 - John Bollinger
我知道它像是一个通用方法,实际上它就是一个带有特殊名称<init>的通用方法。但对于一个通用方法,在实践中通常处理的是一个static方法,因为对于主要的实例方法,它们通常可以使用从类中“继承”的“通用性”来完成所需的操作。我想知道你可能会用通用构造函数做什么,因为构造函数主要初始化状态,很难想象在没有可用于类的通用维度的情况下设置状态的场景... - 0xbe5077ed
1
@keuleJ,我认为你误解了@JohnBollinger的意思。重点是,在编译时和运行时,您的示例中的javacBar(Object you,Object me)完全相同。因此,泛型参数是多余的,并且会稍微令人困惑。而许多使用泛型参数实际上表达了可以在编译时静态验证的有意义的约束条件。 - 0xbe5077ed

8
因为这个非限定泛型类型会被擦除成 Object,所以这与在构造函数中传递 Object 是一样的:
public class Foo {
    Object o;

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

......但是,就像传递一个空的Object一样,除非你做了一些聪明的事情,否则这几乎没有实际价值。

如果您传入绑定的泛型,则可以看到收益和优势,这意味着您实际上可以确保关心的类型。

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

实际上,这更多的是关于灵活性而不是革命性的东西。如果您想要传递特定类别类型的灵活性,则在此处有这样的能力。如果您不需要,则使用标准类没有问题。这仅为您方便而存在,并且由于类型擦除仍然存在,未绑定的泛型与传递 Object 相同(并具有相同的效用)。


2
OP似乎专注于他的类Bar中的通用构造函数,主要是为了对比而呈现通用类Foo。尽管此答案中的所有内容似乎都是正确的,但我很难看出它如何响应通用构造函数问题。 - John Bollinger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接