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

时间:2022-09-02 10:50:33

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

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

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?

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

5 个解决方案

#1


15  

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.

    正如@Lino首先观察到的那样,它允许你表达参数必须与两个或更多不相关类型的组合兼容(当所有但最多只有一个是接口类型时才有意义)。请参阅Lino的答案。

#2


22  

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

我想到的用例可能是某些人想要一个继承自2种类型的Object。例如。实现2个接口:

public class Foo {

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

Though I have never used it in production.

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

#3


19  

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:

在你提供的示例中很清楚U在类的构造函数中没有扮演任何角色,因为它在运行时实际上变成了一个Object:

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!

当然,我从来没有使用过这样的东西,而且我从来没有见过它,但它有可能!

#4


10  

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, 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.

然后,您可以强制执行约束,它们与编译器具有相同的时间。不使U成为全班的通用。

#5


8  

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

因为未绑定的泛型类型会擦除到Object,所以它与在构造函数中传递Object相同:

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.

实际上,这更多的是灵活性,而不是革命性的东西。如果您希望灵活地传递特定类别的类型,那么您可以在此处执行此操作。如果不这样做,那么标准类就没有错。它仅仅是为了您的方便,因为类型擦除仍然是一个东西,未绑定的泛型与传递Object是相同的(并具有相同的实用程序)。

#1


15  

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.

    正如@Lino首先观察到的那样,它允许你表达参数必须与两个或更多不相关类型的组合兼容(当所有但最多只有一个是接口类型时才有意义)。请参阅Lino的答案。

#2


22  

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

我想到的用例可能是某些人想要一个继承自2种类型的Object。例如。实现2个接口:

public class Foo {

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

Though I have never used it in production.

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

#3


19  

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:

在你提供的示例中很清楚U在类的构造函数中没有扮演任何角色,因为它在运行时实际上变成了一个Object:

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!

当然,我从来没有使用过这样的东西,而且我从来没有见过它,但它有可能!

#4


10  

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, 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.

然后,您可以强制执行约束,它们与编译器具有相同的时间。不使U成为全班的通用。

#5


8  

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

因为未绑定的泛型类型会擦除到Object,所以它与在构造函数中传递Object相同:

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.

实际上,这更多的是灵活性,而不是革命性的东西。如果您希望灵活地传递特定类别的类型,那么您可以在此处执行此操作。如果不这样做,那么标准类就没有错。它仅仅是为了您的方便,因为类型擦除仍然是一个东西,未绑定的泛型与传递Object是相同的(并具有相同的实用程序)。