Как определить класс с общим параметром, который расширяет класс с общим параметром?

#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<?> , появится ошибка компиляции, предупреждающая вас, что эта строка небезопасна для типов.