Почему класспредпочтительнее класса - PullRequest
30 голосов
/ 02 декабря 2010

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

Class fooClass;

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

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

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

Class<?> fooClass;

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

РЕДАКТИРОВАТЬ: Почему классобщий?Не могли бы вы привести пример параметризации, т. Е. Может ли быть допустимым использование чего-то другого, кроме <?>?

РЕДАКТИРОВАТЬ: ВАУ!Я не осознал всю глубину этого.Я также наблюдал за Java Puzzler, и он определенно напугал меня по поводу медвежьих ловушек.Поэтому я всегда буду использовать

Class<MyString> myStringClass = MyString.class;

вместо

Class myStringClass = MyString.class;

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

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

Ответы [ 5 ]

44 голосов
/ 02 декабря 2010

Необработанные типы и неограниченные символы

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

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

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

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

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 говорит:

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

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

<ч />

Почему параметризован класс?

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

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
3 голосов
/ 02 декабря 2010

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

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

Class<FooClass> fooClass;

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

Class<? extends FooClass> fooClass;

или

Class<FooInterface> fooClass;

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

2 голосов
/ 02 декабря 2010

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

Class<?> означает «класс неизвестного», где ? - обобщение *1007 символ * .Это означает, что fooClass типа Class<?> принимает Class, тип которого соответствует чему-либо.

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

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

Вы можете эффективно предоставить Parameterized Type длябыть более конкретным, например:

Class<ArrayList> = ArrayList.class;

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

1 голос
/ 02 декабря 2010

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, тогда вам лучше указать тип класса, который вы хотите использовать.Указание ? разрешает все типы, но в некоторых случаях вам нужно быть более конкретным.

1 голос
/ 02 декабря 2010

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...