Подтвердить что ты не робот

Почему класс <?> Предпочитает класс

Если я объявляю класс как поле:

Class fooClass;

Eclipse дает мне предупреждение:

Класс является сырым типом. Ссылки на Общий тип Класс должен быть параметризованная

Что это означает на практике? и почему я призываю это сделать? Если я попрошу Eclipse для "быстрого исправления", он даст мне:

Class<?> fooClass;

который, кажется, не добавляет большого значения, но больше не дает предупреждения.

EDIT: Почему класс является общим? Не могли бы вы привести пример параметризации, т.е. Может ли быть допустимое использование чего-то другого, кроме <?>?

ИЗМЕНИТЬ: WOW! Я не понял глубины этого. Я также смотрел Java Puzzler, и это, конечно, испугало меня о медвежьих ловушках. Поэтому я всегда буду использовать

Class<MyString> myStringClass = MyString.class;

а не

Class myStringClass = MyString.class;

(Но, используя Java с первого дня, я не заметил, когда класс стал общим);

ПРИМЕЧАНИЕ. Я принял @oxbow_lakes, поскольку это имеет смысл для меня, но это, очевидно, очень сложная область. Я настоятельно призывал бы всех программистов использовать конкретный Class<MyString>, а не Class. И Class<?> намного безопаснее, чем Class.

4b9b3361

Ответ 1

Необработанные типы и неограниченные подстановочные знаки

Ни один из предыдущих ответов действительно не решил, почему вы должны предпочесть Class<?> over Class, так как на первый взгляд первая, кажется, предлагает больше информации, чем последняя.

Причина в том, что необработанный тип, т.е. Class, не позволяет компилятору выполнять общие проверки типов. То есть, если вы используете необработанные типы, вы поднимите систему типов. Например:

public void foo(Class<String> c) { System.out.println(c); }

Можно вызвать так (он будет скомпилирован и запущен):

Class r = Integer.class
foo(r); //THIS IS OK (BUT SHOULDN'T BE)

Но не по:

Class<?> w = Integer.class
foo(w); //WILL NOT COMPILE (RIGHTLY SO!)

Всегда используя не сырую форму, даже если вы должны использовать ?, потому что вы не можете знать, что такое параметр типа (или ограничен), вы позволяете компилятору более точно рассуждать о правильности вашей программы чем если бы вы использовали необработанные типы.


Почему у вас есть необработанные типы?

Спецификация Java Language говорит:

Использование типов raw допускается только как уступка совместимости устаревшего кода

Вы всегда должны их избегать. Неограниченный подстановочный знак ?, вероятно, лучше всего описывается в другом месте, но по существу означает, что "это параметрируется на каком-то типе, но я не знаю (или не забочусь) о том, что это такое". Это не то же самое, что сырые типы, которые являются мерзостью и не существуют на других языках с дженериками, например Scala.


Почему параметр Parameterized?

Ну, вот прецедент. Предположим, у меня есть некоторый сервисный интерфейс:

public interface FooService

И я хочу добавить его реализацию, используя системное свойство для определения используемого класса.

Class<?> c = Class.forName(System.getProperty("foo.service"));

В данный момент я не знаю, что мой класс имеет правильный тип:

//next line throws ClassCastException if c is not of a compatible type
Class<? extends FooService> f = c.asSubclass(FooService.class); 

Теперь я могу создать экземпляр FooService:

FooService s = f.newInstance(); //no cast

Ответ 2

который, кажется, не добавляет большого значения но больше не дает предупреждения.

Ты прав. Но это может добавить значение:

Class<FooClass> fooClass;

или, если это более уместно:

Class<? extends FooClass> fooClass;

или

Class<FooInterface> fooClass;

Как и в случае с генериками вообще, вы можете улучшить безопасность типов, указав, какой класс вы хотите сохранить. Предупреждение против сырых типов предназначено только для того, чтобы поймать фрагменты кода, где этот потенциал не используется. Объявив Class<?>, вы в основном говорите: "Это предназначено для хранения любого класса".

Ответ 3

Потому что, поскольку JDK 5, Class теперь имеет параметризованный тип, что делает Class общий объект. Это необходимо (с момента введения Generics), чтобы компилятор выполнял проверку типов (во время компиляции, конечно).

Class<?> означает "класс неизвестности", где ? - это дженерики wildcard. Это означает, что fooClass типа Class<?> принимает Class, тип которого соответствует чему-либо.

Типичный пример:

Class<?> classUnknown = null;
classUnknown = ArrayList.class; //This compiles.

Вы можете, фактически, предоставить параметр с параметризацией более конкретным, например:

Class<ArrayList> = ArrayList.class;

PS Помните, что Class<List> listClass = ArrayList.class; не будет компилироваться (хотя ArrayList имеет список), но (как отметил Марк Питерс в комментарии) Class<? extends List> listClass = ArrayList.class; компилируется (благодаря групповой символ).

Ответ 4

Поскольку использование исходных типов вместо параметризованных типов имеет много ошибок.

Один из них заключается в том, что если используется тип raw, все дженерики класса теряются. Даже те, которые определены для каждого метода.

Ответ 5

Javadoc класса Class дает некоторое представление о том, почему существуют параметры типа для этого класса:

T - тип класса, моделируемого этот объект Class. Например, тип String.class равен Class<String>.Используйте Class<?>, если класс смоделировано неизвестно.

Использование этого параметра не так очевидно, но поверхностный взгляд на исходный код класса указывает, почему параметр типа иногда необходим. Рассмотрим реализацию метода newInstance:

    public T newInstance() 
        throws InstantiationException, IllegalAccessException
    {
    if (System.getSecurityManager() != null) {
        checkMemberAccess(Member.PUBLIC, ClassLoader.getCallerClassLoader());
    }
    return newInstance0();
    }

Если не заметить, тип объекта, возвращаемого этим методом, является типом параметра типа. Это полезно в коде, который использует много отражений, и где нужно быть особенно осторожным, чтобы гарантировать, что объекты правильного типа создаются.

Учитывая пример в вопросе, если экземпляр класса был объявлен как:

Class<String> fooClass;
Class<Integer> barClass;
String aString;

тогда почти невозможно создать следующий код для компиляции:

aString = barClass.newInstance();

Короче говоря, если вы собираетесь работать с иерархиями классов, и вы хотите наложить строгие проверки времени компиляции, чтобы убедиться, что вашему коду не нужно выполнять много проверок instanceof, тогда вам лучше указав тип класса, который вы хотите использовать. Указание ? допускает все типы, но будут случаи, когда вам нужно быть более конкретными.