Я думаю, что один из способов объяснить это - заменить параметр типа фактическим типом.
Параметризованная сигнатура методов:
public static <T extends A & B> T newThing(){
return ...;
}
<T extends A & B>
что называется параметром типа.Компилятор будет ожидать, что это значение фактически будет заменено фактическим типом (называемым аргументом типа), когда вы его фактически используете.
В случае вашего метода фактический тип определяется с помощью вывода типа.То есть <T extends A & B>
должен быть заменен реально существующим типом, который расширяет A и реализует B.
Итак, предположим, что классы C и D одновременно расширяют A и реализуют B, тогда, если ваша подпись была такой:
public static <T extends A & B> T newThing(T obj){
return obj;
}
Тогда, по выводу типа, ваш метод будет оцениваться следующим образом:
public static C newThing(C obj){
return obj;
}
, если вы вызовете с помощью newThing(new C())
.
И будет
public static D newThing(D obj){
return obj;
}
, если вы вызываете с newThing(new D())
.
. Это скомпилируется просто отлично!
Однако, поскольку вы фактически не предоставляете какой-либо тип дляпроверьте вывод типа в объявлении вашего метода, тогда компилятор никогда не сможет быть уверен, каков фактический тип (аргумент типа) вашего параметра типа <T extends A & B>
.
Можно ожидать, что фактическим типом является C, но могут существовать тысячи различных классов, которые удовлетворяют этому критерию.Какие из них следует использовать компилятору в качестве фактического типа аргумента вашего типа?
Допустим, C и D - это два класса, расширяющие A и реализующие B. Какой из этих двух фактических типов должен использовать компилятор в качестве типааргумент для вашего метода?
Вы могли даже объявить аргумент типа, для которого нет даже существующего типа, который вы можете использовать, например, сказать что-то, расширяющее Serializable и Closable и Comparable and Appendable.
И, возможно, во всем мире нет класса, который бы удовлетворял это.
Таким образом, вы должны понимать, что параметр типа здесь - это просто требование к компилятору проверять фактический тип, который вы используете, заполнитель для фактического типа;и этот фактический тип должен существовать в конце, и компилятор будет использовать его для замены появлений T. Поэтому фактический тип (аргумент типа) должен быть выводимым из контекста.
Поскольку компилятор не может с уверенностью сказать, какойэто фактический тип, который вы имеете в виду, в основном потому, что в данном случае нет способа определить это путем вывода типа, тогда вы вынуждены привести тип к типу, чтобы убедиться, что компилятор знает, что вы делаете.
Таким образом, вы можете реализовать свой метод, используя вывод типа следующим образом:
public static <T extends A & B> T newThing(Class<T> t) throws Exception{
return t.newInstance();
}
Таким образом, вы на самом деле будете сообщать компилятору, какой фактический аргумент типа будет использоваться.
Примите во внимание, что когда генерируются байт-коды, компилятор должен заменить T для реального типа.Нет никакого способа написать метод в Java, подобный этому
public static A & B newThing(){ return ... }
Верно?
Надеюсь, я объяснил сам!Это не просто объяснить.