Предполагая, что у меня есть класс с двумя переменными экземпляра родового типа, как я могу заставить эти типы быть одинаковыми?
Я не нашел подобных вопросов в другом месте (не говоря уже о ответах), что может быть связано с тем, что я неправильно использую термины. Вот этот вопрос, но я не уверен, как применить его к моей ситуации.
Короче говоря, как я могу заставить эту последнюю строку кода не компилироваться?
class Foo<T> {
private T value;
}
class Bar {
private Foo var1;
private Foo var2;
public Bar(Foo var1, Foo var2) {
this.var1 = var1;
this.var2 = var2;
}
public static void main(String[] args) {
Foo<String> var1 = new Foo<>();
Foo<Integer> var2 = new Foo<>();
Bar b = new Bar(var1, var2); // this should not work
}
}
Ответ 1
Make Bar
также общий:
class Bar<T> {
private Foo<T> var1;
private Foo<T> var2;
public Bar(Foo<T> var1, Foo<T> var2) {
this.var1 = var1;
this.var2 = var2;
}
public static void main(String[] args) {
Foo<String> var1 = new Foo<>();
Foo<Integer> var2 = new Foo<>();
Bar<String> b = new Bar<>(var1, var2); // this does not work
}
}
Используя общий параметр T
для класса Bar
вы можете применять одни и те же типы для обеих переменных экземпляра.
Это устраняет также предупреждение использования необработанных типов (которые никогда не должны использоваться), как упоминалось в комментариях @daniu.
Теперь, если вы не используете необработанные типы, но хотите разрешить не одни и те же типы, вы можете работать с подстановочными знаками:
С верхним ограничением, которое допускает только типизированное чтение (var1
и var2
всегда будут производить реализацию типа T
):
class Bar<T> {
private Foo<? extends T> var1;
private Foo<? extends T> var2;
public Bar(Foo<? extends T> var1, Foo<? extends T> var2) {
this.var1 = var1;
this.var2 = var2;
}
public static void main(String[] args) {
Foo<String> var1 = new Foo<>();
Foo<Integer> var2 = new Foo<>();
Bar<Object> b = new Bar<>(var1, var2); // this does now work
}
}
И с более низким ограничением, которое допускает только написание записи (var1
и var2
всегда будут использовать любую реализацию типа T
):
class Bar<T> {
private Foo<? super T> var1;
private Foo<? super T> var2;
public Bar(Foo<? super T> var1, Foo<? super T> var2) {
this.var1 = var1;
this.var2 = var2;
}
public static void main(String[] args) {
Foo<Integer> var1 = new Foo<>();
Foo<Number> var2 = new Foo<>();
Bar<Integer> b = new Bar<>(var1, var2); // this does now work
}
}
Подробнее об этой теме вы можете прочитать: Что такое PECS (продюсер продлевает потребительский супер)?
Ответ 2
Ответ Лино хороший. Я хочу добавить один факт:
Если вам не нужно отслеживать тип var
s, но только убедитесь, что они одного типа, то вы можете перенести параметр типа из класса Bar
в его конструктор:
class Bar {
// These will always have the same type, but
// that not visible here
private Foo<?> var1;
private Foo<?> var2;
// The parameter types of the construct ensures that
// the vars are always of the same type
public <T> Bar(Foo<T> var1, Foo<T> var2) {
this.var1 = var1;
this.var2 = var2;
}
public static void main(String[] args) {
Foo<String> var1 = new Foo<>();
Foo<Integer> var2 = new Foo<>();
Bar b1 = new Bar(var1, var1); // Works fine
Bar b2 = new Bar(var1, var2); // Compilation error
}
}
Этот метод работает для всех видов методов.