Зачем мне использовать java.ленг.Класс.cast [дубликат]

Возможные Дубликаты:
когда я должен использовать метод java 5 cast класса?

недавно я наткнулся на фрагмент кода, который выглядел так:

Object o = .. ;
Foo foo = Foo.class.cast(o);

Я даже не знал, что java.ленг.У класса был метод cast, поэтому я заглянул в документы, и из того, что я понимаю, это просто делает приведение к классу, который представляет объект Class. Таким образом, приведенный выше код будет примерно эквивалентен

Object o = ..;
Foo foo = (Foo)o;

поэтому я задался вопросом, почему я хотел бы использовать метод cast вместо того, чтобы просто делать бросок "по-старому". Есть ли у кого-нибудь хороший пример, когда использование метода cast выгодно по сравнению с простым литьем?

9 ответов


С Java-Класса.cast () против оператора cast

Я только когда-либо использовать класс.cast (объект), чтобы избежать предупреждений в "generics land". Я часто вижу, как методы делают такие вещи:

@SuppressWarnings("unchecked")
<T> T doSomething() {
Object o;
// snip
return (T) o;
}  

часто лучше всего заменить его на

<T> T doSomething(Class<T> cls) {
Object o;
// snip
return cls.cast(o);
}  

Это единственный usecase для класса.cast (объект) я когда-либо сталкивался.


Я не думаю, что это часто используется именно так, как вы показали. Наиболее распространенное использование, которое я видел, - это то, где люди, использующие дженерики, пытаются сделать эквивалент этого:

public static <T extends Number> T castToNumber(Object o) {
    return (T)o;
}

который на самом деле не делает ничего полезного из-за стирания типа.

тогда как это работает и является безопасным (по модулю ClassCastExceptions):

public static <T extends Number> T castToNumber(Object o, Class<T> clazz) {
    return clazz.cast(o);
}

редактировать: несколько примеров использования от google гуава:


в Java часто существует более одного способа освежевать кошку. Такая функциональность может быть полезна в случаях, когда у вас есть код фреймворка. Представьте себе метод, который принимает экземпляр объекта класса и экземпляр объекта и возвращает случай объекта как класс:

public static void doSomething(Class<? extends SomeBaseClass> whatToCastAs,Object o)
    {
        SomeBaseClass castObj =  whatToCastAs.cast(o);
        castObj.doSomething();
    }

В общем случае используйте более простое литье, если этого недостаточно.


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


нет абсолютно никаких причин писать Foo.class.cast(o), Это эквивалентно (Foo)o.

в общем, если X - это reifiable типа и Class<X> clazz, потом clazz.cast(o) это то же самое, что (X)o.

если все типы reifiable, метод Class.cast() поэтому избыточно и бесполезно.

к сожалению, из-за стирания в текущей версии Java не все типы поддаются проверке. Например, переменные типа не reifiable.

если T является переменной типа, cast (T)o не установлен, потому что во время выполнения точный тип T неизвестно JVM, JVM не может проверить, если o - это действительно типа T. Бросание может быть разрешено ошибочно, что может вызвать проблемы позже.

это не огромная проблема, обычно когда программист делает (T)o, он уже рассуждал, что бросок безопасен и не вызовет никаких проблем во время выполнения. Актерский состав проверил по app логика.

предположим Class<T> clazz доступен в точке броска, то мы знаем, что T во время выполнения, мы можем добавить дополнительные проверки во время выполнения, чтобы убедиться, что o действительно T.

check clazz.isInstance(o);
(T)o;

и это по сути то, что Class.cast() делает.

мы никогда не ожидали бы, что бросок потерпит неудачу в любом случае, поэтому в правильно реализованном приложении проверьте clazz.isInstance(o) должен всегда преуспевать anway, поэтому clazz.cast(o) эквивалентно (T)o - опять же, под предположение, что код правильный.

если можно доказать, что код верен и приведение безопасно, можно предпочесть (T)o to clazz.cast(o) по причине производительности. В Примере MutableClassToInstanceMap поднятый в другом ответе, мы можем видеть, очевидно, что бросок безопасен, поэтому простой (T)o хватило бы.


класса.бросание конструировано для типа дженериков.

когда вы строите класс с общим параметром T, вы можете передать Класс. Затем вы можете выполнить приведение как со статическим, так и с динамическим проверка, которая (T) не дает вам. Он также не производит unchecked предупреждения, потому что он проверен (в этот момент).


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

один пример для этого - когда вы получаете коллекцию из DAO, которая возвращает непроверенную коллекцию, и на вашем сервисе вы повторяете это, эта ситуация может привести к ClassCastException.

один из способов его решения, поскольку у вас есть класс wanted и непроверенная коллекция, - это итерация над ним и приведение его внутри DAO, преобразующего коллекцию в проверенную коллекцию, а затем возвращает ее.


потому что у вас может быть что-то такое:

Number fooMethod(Class<? extends Number> clazz) {
    return clazz.cast(var);
}

a "cast" на Java, например (Number)var, где вещь внутри скобок является ссылочным типом, действительно состоит из двух частей:

  • время компиляции: результат выражения cast имеет тип типа, который вы бросили в
  • время выполнения: он вставляет операцию проверки, которая в основном говорит, что если объект не является экземпляром этого класса, то бросьте ClassCast Exception (Если вещь, которую вы бросаете, является переменной типа, то класс, который она проверяет, будет нижней границей переменная типа)

чтобы использовать синтаксис, вам нужно знать класс во время написания кода. Предположим, вы не знаете во время компиляции, к какому классу вы хотите привести; вы знаете это только во время выполнения.

теперь вы спросите, тогда в чем смысл кастинга? Не является ли точка приведения, чтобы превратить выражение в желаемый тип во время компиляции? Так что если вы не знаете тип во время компиляции, то нет никакой пользы во время компиляции, верно? Правда, но это только первый пункт выше. Вы забываете компонент времени выполнения приведения (второй элемент выше): он проверяет объект против класса.

таким образом, цель приведения среды выполнения (т. е. Class.cast()) - это проверка того, что объект является экземпляром класса, А если нет, создайте исключение. Это примерно эквивалентно этому, но короче:

if (!clazz.isInstance(var))
    throw new ClassCastException();

некоторые люди упоминали об этом Class.cast() также имеет хороший тип возврата, основанный на параметре type переданного класса in, но это всего лишь функция времени компиляции, которая в любом случае предоставляется во время компиляции. Поэтому для этой цели нет смысла использовать Class.cast().