Какую цель выполняют общие конструкторы в Java?
Как известно, у вас может быть общий класс в Java с помощью аргументов типа:
class Foo<T> {
T tee;
Foo(T tee) {
this.tee = tee;
}
}
Но вы также можете иметь общие конструкторы, означающие конструкторы, которые явно получают свои собственные аргументы общего типа, например:
class Bar {
<U> Bar(U you) {
// Why!?
}
}
Я изо всех сил пытаюсь понять прецедент. Что эта функция позволяет мне делать?
Ответы
Ответ 1
Что эта функция позволяет мне делать?
Есть, по крайней мере, three две вещи, которые он позволяет вам делать, иначе вы не могли:
-
выражают отношения между типами аргументов, например:
class Bar {
<T> Bar(T object, Class<T> type) {
// 'type' must represent a class to which 'object' is assignable,
// albeit not necessarily 'object' exact class.
// ...
}
}
-
< отозванa >
-
Как заметил @Lino, он позволяет вам выразить, что аргументы должны быть совместимы с комбинацией из двух или более несвязанных типов (что может иметь смысл, когда все, но не более одного, являются типами интерфейсов). См. Ответ Lino для примера.
Ответ 2
Пример использования, о котором я думаю, может состоять в том, что некоторым нужен объект, который наследуется от 2 типов. Например. реализует 2 interfaces
:
public class Foo {
public <T extends Bar & Baz> Foo(T barAndBaz){
barAndBaz.barMethod();
barAndBaz.bazMethod();
}
}
Хотя я никогда не использовал его в производстве.
Ответ 3
В примере показано, что 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!
}
}
Обратите внимание, что класс уже использует другой общий тип; это позволяет использовать отдельный, неродственный общий тип для определения класса.
Конечно, я никогда не использовал что-то подобное, и я никогда не видел его в использовании, но это возможно!
Ответ 4
Собственно, этот конструктор
class Bar {
<U> Bar(U you) {
// Why!?
}
}
аналогичен общий метод. Было бы гораздо больше смысла, если бы у вас было несколько аргументов конструктора:
class Bar {
<U> Bar(U you, U me) {
// Why!?
}
}
Затем вы можете применить ограничение, чтобы они имели одинаковое время с компилятором. Не делая U общим для всего класса.
Ответ 5
Поскольку этот несвязанный общий тип стирает до 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
.