Если вы создаете общий класс в Java (у класса есть параметры типового типа), можете ли вы использовать общие методы (метод принимает общие параметры типа)?
Рассмотрим следующий пример:
public class MyClass {
public <K> K doSomething(K k){
return k;
}
}
public class MyGenericClass<T> {
public <K> K doSomething(K k){
return k;
}
public <K> List<K> makeSingletonList(K k){
return Collections.singletonList(k);
}
}
Как и следовало ожидать с помощью общего метода, я могу вызвать doSomething(K)
в экземплярах MyClass
с любым объектом:
MyClass clazz = new MyClass();
String string = clazz.doSomething("String");
Integer integer = clazz.doSomething(1);
Однако, если я попытаюсь использовать экземпляры MyGenericClass
без указания общего типа,
Я называю doSomething(K)
возвращает Object
, вне зависимости от того, что было передано K
:
MyGenericClass untyped = new MyGenericClass();
// this doesn't compile - "Incompatible types. Required: String, Found: Object"
String string = untyped.doSomething("String");
Как ни странно, он будет компилироваться, если тип возвращаемого значения является общим классом - например. List<K>
(На самом деле это можно объяснить - см. ответ ниже):
MyGenericClass untyped = new MyGenericClass();
List<String> list = untyped.makeSingletonList("String"); // this compiles
Кроме того, он будет скомпилирован, если типичный класс будет напечатан, даже если только с подстановочными знаками:
MyGenericClass<?> wildcard = new MyGenericClass();
String string = wildcard.doSomething("String"); // this compiles
-
Есть ли веская причина, почему вызов универсального метода в нетипизированном родовом классе не должен работать?
-
Есть ли какой-нибудь умный трюк, относящийся к общим классам и общим методам, которые мне не хватает?
EDIT:
Чтобы уточнить, я ожидал бы, что нетипизированный или не типичный типичный класс не будет соблюдать общие параметры типа класса (поскольку они не были предоставлены). Однако мне непонятно, почему нетипизированный или не типичный типичный класс будет означать, что общие методы не соблюдаются.
Понятно, что этот вопрос уже поднят на SO, c.f. этот вопрос. Ответы на это объясняют, что, когда класс является нетипизированным/в его исходной форме, все дженерики удаляются из класса, включая типизацию общих методов.
Однако на самом деле нет объяснений, почему это так. Поэтому позвольте мне уточнить мой вопрос:
- Почему Java удаляет типичный метод ввода на нетипизированных или нестандартных классах? Есть ли веская причина для этого, или это просто недосмотр?
EDIT - обсуждение JLS:
Было высказано предположение (в ответ на предыдущий вопрос SO и на этот вопрос), что это рассматривается в JLS 4.8, в котором говорится
Тип конструктора (§8.8), метод экземпляра (§8.4, п. 9.4) или нестатического поля (§8.3) М необработанного типа С, который не унаследован от его суперклассов или суперинтерфейсов, является исходным тип, соответствующий стиранию его типа в общем объявлении, соответствующем C.
Мне ясно, как это относится к нетипизированному классу - типовые типы класса заменяются типами стирания. Если обобщенные обобщенные классы связаны, то тип стирания соответствует этим границам. Если они не связаны, то тип стирания является объектом - например.
// unbound class types
public class MyGenericClass<T> {
public T doSomething(T t) { return t; }
}
MyGenericClass untyped = new MyGenericClass();
Object t = untyped.doSomething("String");
// bound class types
public class MyBoundedGenericClass<T extends Number> {
public T doSomething(T t) { return t; }
}
MyBoundedGenericClass bounded = new MyBoundedGenericClass();
Object t1 = bounded.doSomething("String"); // does not compile
Number t2 = bounded.doSomething(1); // does compile
Хотя общие методы являются методами экземпляра, мне не ясно, что JLS 4.8 применяется к общим методам. Тип обобщенного метода (<K>
в более раннем примере) не является нетипизированным, так как его тип определяется параметрами метода - только класс нетипирован/необработанный.