Многие люди не понимают, что они могут сравнивать строки, используя: OrdinalIgnoreCase вместо необходимости выполнять someString.ToUpper (). Это устраняет дополнительные накладные расходы на выделение строк.
if( myString.ToUpper() == theirString.ToUpper() ){ ... }
становится
if( myString.Equals( theirString, StringComparison.OrdinalIgnoreCase ) ){ ... }
Вторая версия интерфейса неверна. В нем говорится, что getValue
вернет любой подкласс, который вы запрашиваете - тип возвращаемого значения будет выведен на основе выражения в левой руке.
Итак, если вы получите ссылку на x
(назовем его obj
), вы можете законно сделать следующий вызов без предупреждений компилятора:
x obj = ...;
B b = obj.getValue();
Что, вероятно, неверно в вашем примере, потому что если вы добавите другой класс C
, который также расширяет A
, вы также можете законно сделать вызов:
C c = obj.getValue();
Это потому, что T
не является переменной типа, принадлежащей интерфейсу, а только самому методу.
В основном, когда вы выполняете
, вы говорите, что вам нужен конкретный подкласс A, а не какой-либо A, потому что тогда вы могли бы просто сделать:
A getValue();
Компилятор предупреждает вас, что он не может гарантировать, что будет возвращен конкретный определенный подкласс A, только сам A.
РЕДАКТИРОВАТЬ: Чтобы попытаться объяснить это лучше, B является подклассом A, но не t должен быть единственным подклассом A. Рассмотрим следующую иерархию.
B расширяет A C расширяет A, но не B
Теперь у меня есть метод:
public void someMethod(x value) {
C c = x.getValue();
}
Следуя универсальным шаблонам, он должен компилироваться и гарантировать отсутствие исключения приведения класса во время выполнения (здесь могут быть другие проблемы с интерфейсами, но, безусловно, есть случаи, которые можно использовать чтобы продемонстрировать это правильно - я придерживаюсь вашего примера).
Но что, если вы передадите экземпляр y этому методу?
Что ж, теперь я получаю B вместо C. предупреждает, что это могло произойти.
Компилятор сообщает вам, что нет никакого способа разрешить общий. T нигде не определен, кроме как output - о каком T вы говорите? Если бы интерфейс был сгенерирован как тип T:
interface x <T extends A> {
T getValue()
}
, тогда это сработало бы, или если бы метод принял значение, которое определило бы, какой тип возврата вы хотите:
interface x {
<T extends A> T getValue(T someArgument)
}
или
interface x {
<T extends A> T getValue(Class<T> someArgument)
}
, тогда предупреждения не будет.
править: См. Сообщение свиристеля для объяснения того, почему вы НЕ должны использовать дженерики для этой конкретной проблемы. Я просто показываю, насколько подходят дженерики.
Итак, чтобы опираться на два ответа от AlbertoPL и Yishai, попробуйте следующее:
class y implements x {
A getValue(){ return new B();}
}
B расширяет A, так что это нормально. Вызывающему не нужно знать, какой подкласс A возвращается, только то, что он расширяет A. Итак, мы идем :)