#java #generics #inheritance
#java #общие #наследование
Вопрос:
Рассмотрим следующий случай:
public class PointCloud<T extends Point> { }
public class PointCloud3d extends PointCloud<Point3d> { }
Как мне написать класс, который принимает общий параметр T
, являющийся расширением PointCloud<T>
?
public class PointCloudReader<T extends PointCloud> {
T read(String path);
}
или
public class PointCloudReader<T extends PointCloud<?>> {
T read(String path);
}
В чем разница между двумя определениями классов?
Комментарии:
1. Правильный способ сделать это, вероятно, действительно
public class PointCloudReader<P extends Point, T extends PointCloud<P>>
2. @LouisWasserman Я действительно думаю, что это тот ответ, который мне нужен.
3. @GiorgiTsiklauri У меня есть проект JNI, и работать с наследованием и обобщениями намного сложнее, чем если бы я работал только с Java.
Ответ №1:
Ни то, ни другое.
Используйте это:
public class PointCloudReader<P extends Point, T extends PointClound<P>> {
T read(String path);
}
Это бесполезно, потому что он использует необработанный тип PointCloud
и все равно не работает:
public class PointCloudReader<T extends PointCloud> {
T read(String path);
}
Это бесполезно, потому что он использует неизвестный тип PointCloud
и все равно не работает:
public class PointCloudReader<T extends PointCloud<?>> {
T read(String path);
}
Комментарии:
1. Java не может вывести границу T из суперкласса. Вы должны его переформулировать. Это должно быть
class PointCloudReader<T extends Point> extends PointCloud<T>
: ideone.com/fNSkyi . Я взял на себя смелость обновить его. Если вы не согласны, дайте мне знать2. Спасибо, но разве вы не имеете в виду:
public class PointCloudReader<T extends Point> { PointCloud<T> read(String path); }
3. Я использую JNI, поэтому дженерики вызывают у меня небольшие проблемы…
4. Я думаю, что комментарий @LouisWasserman делает это за меня.
5. @VladAdrianMoglan Я изначально сам закодировал предложение Луи, но упростил его, потому что это не казалось необходимым, я вернул его обратно.
Ответ №2:
Разница в том, что в первом случае вы используете необработанный тип, который не рекомендуется. Во втором случае вы используете подстановочный знак, который более безопасен для типов.
например, рассмотрим следующий код:
class Test <T extends ArrayList<?>>{
void test(T t) {
Object o=t.get(0);
t.add("someStr"); //this line will be a compile error
}
}
Если вы используете <T extends ArrayList>
, вы не получите никакой ошибки компиляции, но если вы используете ArrayList неправильного типа, во время выполнения будет выдано исключение.
Но если вы используете <T extends ArrayList<?>
, появится ошибка компиляции, предупреждающая вас, что эта строка небезопасна для типов.