Пусть я 2 класса под названием RealNumber
и IntNumber
и метод называется plus
:В этой иерархии классов Java, как использовать Generics, чтобы приведение типов было ненужным?
public class RealNumber<S extends RealNumber> {
public S plus(S realNumber) {
...
}
}
public class IntNumber
extends RealNumber<IntNumber> { }
При использовании метода plus
я получаю некоторые предупреждения компилятора и ошибки:
RealNumber x = new RealNumber();
IntNumber y = new IntNumber();
RealNumber sum1 = x.plus(x); // Warning: Unchecked call to 'plus(S)' as a member of raw type 'RealNumber'.
RealNumber sum2 = x.plus(y); // Warning: Unchecked call to 'plus(S)' as a member of raw type 'RealNumber'.
RealNumber sum3 = y.plus(x); // Error: plus (IntNumber) in RealNumber cannot be applied to (RealNumber).
RealNumber sum4 = y.plus(y); // This works fine.
IntNumber sum5 = y.plus(x); // Error: plus (IntNumber) in RealNumber cannot be applied to (RealNumber).
IntNumber sum6 = y.plus(y); // This works fine.
Чтобы это исправить, я изменить метод plus
следующим образом:
public <T extends RealNumber> S plus(T realNumber) {
...
}
И теперь все работает отлично. Хорошо! Однако, я хочу сейчас, чтобы добавить третий класс под названием PositiveIntNumber
, расширяющий IntNumber
:
public class PositiveIntNumber extends IntNumber { }
Это, конечно, не работает:
RealNumber x = new RealNumber();
IntNumber y = new IntNumber();
PositiveIntNumber z = new PositiveIntNumber();
RealNumber sum1 = x.plus(x); // Fine.
RealNumber sum2 = x.plus(y); // Fine.
RealNumber sum3 = y.plus(x); // Fine.
RealNumber sum4 = y.plus(y); // Fine.
IntNumber sum5 = y.plus(x); // Fine.
IntNumber sum6 = y.plus(y); // Fine.
RealNumber sum7 = x.plus(z); // Fine.
IntNumber sum8 = y.plus(z); // Fine.
PositiveIntNumber sum9 = z.plus(x); // Error: incompatible types: no instance(s) of type variable(s) T exist so that IntNumber conforms to PositiveIntNumber
PositiveIntNumber sum10 = z.plus(y); // Error: incompatible types: no instance(s) of type variable(s) T exist so that IntNumber conforms to PositiveIntNumber
PositiveIntNumber sum11 = z.plus(z); // Error: incompatible types: no instance(s) of type variable(s) T exist so that IntNumber conforms to PositiveIntNumber
Чтобы исправить это снова, я изменять определения классов, следующим образом:
public class IntNumber<S extends IntNumber>
extends RealNumber<S> { }
public class PositiveIntNumber
extends IntNumber<PositiveIntNumber>
{ }
Это решает проблему PositiveIntNumber
, но ломает IntNumber
:
RealNumber x = new RealNumber();
IntNumber y = new IntNumber();
PositiveIntNumber z = new PositiveIntNumber();
RealNumber sum1 = x.plus(x); // Fine.
RealNumber sum2 = x.plus(y); // Fine.
RealNumber sum3 = y.plus(x); // Fine.
RealNumber sum4 = y.plus(y); // Fine.
IntNumber sum5 = y.plus(x); // Error: incompatible types: RealNumber cannot be converted to IntNumber.
IntNumber sum6 = y.plus(y); // Error: incompatible types: RealNumber cannot be converted to IntNumber.
RealNumber sum7 = x.plus(z); // Fine.
IntNumber sum8 = y.plus(z); // Error: incompatible types: RealNumber cannot be converted to IntNumber.
PositiveIntNumber sum9 = z.plus(x); // Fine.
PositiveIntNumber sum10 = z.plus(y); // Fine.
PositiveIntNumber sum11 = z.plus(z); // Fine.
Литая фиксирует это, но это не должно быть необходимым, на мой взгляд:
IntNumber sum5 = (IntNumber)y.plus(x);
Итак, у меня есть два вопроса:
1) Поскольку y
является IntNumber
, и возвращаемый тип S
extends IntNumber
, почему y.plus (...) return RealNumber
?
2) Как исправить это?
Edit: Написание RealNumber<RealNumber>
должно быть ненужным, так как RealNumber<IntNumber>
и RealNumber<PositiveIntNumber>
не делают абсолютно никакого смысла. Поэтому, возможно, полное использование таких дженериков просто неверно. Я думаю, что это отвечает на вопрос 1: y.plus(...)
возвращает RealNumber
, потому что y является сырым, поэтому система типа Java просто больше не заботится о том, чтобы S
расширяет IntNumber
. Однако я хочу избежать повторения метода plus
во всех подклассах RealNumber, и я должен каким-то образом использовать дженерики, чтобы этого избежать. Таким образом, вопрос 2 все еще стоит. Что мне делать?
Примечание: Мои фактические классы не являются действительными и целыми, а некоторые другие сложные бизнес-классы. Думайте о них как о классах A, B, C и не ставьте под сомнение модель. Они являются «добавочными», да, но здесь X.plus (Y) должен возвращать тип X для каждого X и Y.
Для одного, 'RealNumber' является общим. Параметрируйте его использование. Не используйте необработанные типы. –
Вы должны обратить больше внимания на предупреждение, подобное этому * «Предупреждение: Непроверенный вызов« плюс (S) »в качестве члена необработанного типа« RealNumber ». * *. Как и это предупреждение, и @SotiriosDelimanolis говорит вам, не используйте вместо него исходный «RealNumber» Parameterize: «RealNumber x = new RealNumber <>();' –
Tom
Я не получаю 'sum5'. Почему «IntNumber» плюс «RealNumber» дают «IntNumber»? –