У меня есть еще одна интересная проблема с Java Generics.Я уверен, что некоторые из вас могли бы иметь правильный ответ на него.
Контекст. Сначала, чтобы понять контекст, давайте посмотрим на определение объектов Animal, Mammal, иКошка, которая определяется друг с другом:
public abstract class Animal<T extends Animal<T>> {
private final int age;
public Animal(int age) {
this.age = age;
}
public int getAge() {
return this.age;
}
public abstract T setAge(int age);
public static void main(String[] args) {
List<Mammal<?>> mammals = new ArrayList<Mammal<?>>();
mammals = Animal.increaseAge(mammals, 1);
Map<Integer, List<Mammal<?>>> mammalsByAge = Animal.groupByAge(mammals);
}
public static <T extends Animal<T>> List<T> increaseAge(List<T> animals, int augment) {
List<T> list = new LinkedList<T>();
for (T animal : animals) {
list.add(animal.setAge(animal.getAge() + augment));
}
return list;
}
public static <T extends Animal<T>> Map<Integer, List<T>> groupByAge(List<T> animals) {
Map<Integer, List<T>> animalsPerAge = new TreeMap<Integer, List<T>>();
animals.forEach((animal) -> {
int age = animal.getAge();
animalsPerAge.putIfAbsent(age, new LinkedList<T>());
animalsPerAge.get(age).add(animal);
});
return animalsPerAge;
}
};
abstract class Mammal<T extends Mammal<T>> extends Animal<Mammal<T>> {
public Mammal(int age) {
super(age);
}
};
class Cat extends Mammal<Cat> {
public Cat(int age) {
super(age);
}
@Override
public Cat setAge(int age) {
return new Cat(age);
}
}
Позже у меня будут разные животные и млекопитающие, очевидно.Кроме того, при импорте все элементы должны реализовывать политику «копирования при записи», т. Е. При изменении свойства животного создается новое животное того же типа с новым свойством.
Наконец, мне нужнореализовать две вспомогательные функции, groupByAge
и increaseAge
.
Проблема. Вначале я понял, что Eclipse IDE может компилировать и выполнять программу, тогда как компилятор Mavenне удалось с ошибкой компилятора.
[ERROR] Failed to execute goal
org.apache.maven.plugins:maven-compiler-plugin:3.1:compile
(default-compile) on project BCBS248Calculator: Compilation failure:
Compilation failure:
[ERROR] Animal.java:[25,33] method increaseAge in class
com.ibm.ilm.bcbs248.Animal<Tcannot be applied to given types;
[ERROR] required: java.util.List<T>,int
[ERROR] found: java.util.List<com.ibm.ilm.bcbs248.Mammal<?>>,int
[ERROR] reason: inferred type does not conform to equality
constraint(s)
[ERROR] inferred: com.ibm.ilm.bcbs248.Mammal<capture#1 of ?>
[ERROR] equality constraints(s): com.ibm.ilm.bcbs248.Mammal<capture#1
of ?>,com.ibm.ilm.bcbs248.Mammal<?>
[ERROR] Animal.java:[27,68] method groupByAge in class
com.ibm.ilm.bcbs248.Animal<Tcannot be applied to given types;
[ERROR] required: java.util.List<T>
[ERROR] found: java.util.List<com.ibm.ilm.bcbs248.Mammal<?>>
[ERROR] reason: inferred type does not conform to equality
constraint(s)
[ERROR] inferred: com.ibm.ilm.bcbs248.Mammal<capture#2 of ?>
[ERROR] equality constraints(s): com.ibm.ilm.bcbs248.Mammal<capture#2
of ?>,com.ibm.ilm.bcbs248.Mammal<?>
На первый взгляд, я думал, что это проблема Maven (потому что Eclipse может компилировать и выполнять код), но в действительности это, похоже, ошибка (?) в компиляторе Eclipse.Даже обычный Java-компилятор не может скомпилировать программу, он заканчивается ошибкой типа, поскольку программа не может быть проверена.Это хорошо известная проблема с обобщениями Java, то есть средство проверки типов Java не может проверить программу на данном этапе.Это просто из-за приближения статической проверки типов.
Насколько я знаю, Eclipse использует собственный компилятор Java, то есть оболочку вокруг оригинального компилятора Java, которая позволяла компилировать вредоносные программы?Тем не менее, в этой ситуации, это не показывает сообщение об ошибке?Кто-нибудь может проверить, является ли это проблемой с компилятором Eclipse?
Кроме того, у кого-нибудь есть еще одна изящная идея о том, как решить проблему кодирования?Проще говоря, я могу изменить сигнатуру типа обеих вспомогательных функций следующим образом:
public static <T extends Animal<?>> List<T> increaseAge(List<T> animals, int augment) {
List<T> list = new LinkedList<T>();
for (T animal : animals) {
list.add((T) animal.setAge(animal.getAge() + augment));
}
return list;
}
public static <T extends Animal<?>> Map<Integer, List<T>> groupByAge(List<T> animals) {
Map<Integer, List<T>> animalsPerAge = new TreeMap<Integer, List<T>>();
animals.forEach((animal) -> {
int age = animal.getAge();
animalsPerAge.putIfAbsent(age, new LinkedList<T>());
animalsPerAge.get(age).add(animal);
});
return animalsPerAge;
}
К сожалению, мне также нужно добавить приведение типа в строку list.add((T) animal.setAge(animal.getAge() + augment));
.Я могу жить с измененной сигнатурой типа, но я избавлюсь от трансляции типов, поскольку она заканчивается неопределенностью.
Повторное добавление элемента того же типа, похоже, не является проблемой с изменениями сигнатуры типа,Однако после вызова setAge
некоторая информация, похоже, теряется.Может быть, я могу изменить тип функции или объекта таким образом, чтобы это работало?
-
Maven версия 3.1
Java версия 1.8.0_201 Java (TM)Среда выполнения SE (сборка 1.8.0_201-b09) 64-разрядная серверная виртуальная машина Java HotSpot (TM) (сборка 25.201-b09, смешанный режим)
Eclipse IDE для разработчиков Java Версия: выпуск Oxygen.3a (4.7.3a) Идентификатор сборки: 20180405-1200