Тип безопасности с дженериками в Java

Я столкнулся с поведением дженериков в Java, которые я полностью не могу понять (с моим фоном .NET).

public class TestGeneric<T>
{
    public void get (Object arg)
    {
        T temp = (T) arg;

        System.out.println(temp.toString());

        return;
    }
}

TestGeneric<Integer> tg = new TestGeneric<Integer>();
tg.get("Crack!!!");

Скажите, пожалуйста, почему я не получаю ClassCastException в том, что в Idea я вижу temp как String после присваивания и имеющий значение "Crack!!!". Кроме того, как я могу удалить это ClassCastException? Я использую JDK 1.7.0_07 для Windows 7 x64.

Ответ 1

Причина, по которой вы не получаете исключение класса, заключается в том, что Java-дженерики реализуются посредством стирания типа. В отличие от общих дженериков .NET, которые требовали значительных изменений в CLS, генерические файлы Java обрабатываются полностью во время компиляции. Во время выполнения приведение к T игнорируется. Чтобы проверить тип во время выполнения, вам нужно сохранить Class<T> и использовать его методы для проверки типа передаваемого параметра:

public class TestGeneric<T>
{
    private Class<T> genClass;
    public TestGeneric(Class<T> t) {genClass = t;}
    public void get (Object arg)
    {
        if (!genClass.isInstance(arg)) {
            throw new ClassCastException();
        }
        T temp = (T) arg;

        System.out.println(temp.toString());

        return;
    }
}

TestGeneric<Integer> tg = new TestGeneric<Integer>(Integer.class);
tg.get("Bang!!!"); // Now that a real Bang!!!

Ответ 2

Это связано с тем, что общий тип T не имеет определенных границ, поэтому он рассматривается как объект. В этом случае приведение чего-то к T не вызовет ClassCastException.

Однако, если ваше определение класса было public class TestGeneric<T extends Number>, тогда вы получите ClassCastException, если вы передали в String в get().

Ответ 3

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

public class TestGeneric
{
    public void get (Object arg)
    {
        Object temp = (Object) arg;

        System.out.println(temp.toString());

        return;
    }
}

TestGeneric tg = new TestGeneric();
tg.get("Crack!!!"); // should there be any problem?

Ответ 4

Это связано с type-erasure. Это означает, что в Java все дженерики сводятся к Object во время выполнения. Таким образом, вы отбросили ваш String до Object, который полностью прекрасен. А так как toString реализован на Object, опять же исключение.

Вот ссылка на тип-стирание

Единственный способ получить ClassCastException - передать экземпляр Class<T> в общий тип, а затем сделать myClass.isInstance(arg) и выбросить исключение, если false

Ответ 5

Всегда помните, что Generics в Java - это объекты времени компиляции. Во время выполнения он не имеет ничего общего. Позвольте мне продемонстрировать вам ваш код oqn.

public class TestGeneric<T>
{
    public void get (Object arg)
    {
        T temp = (T) arg;

        System.out.println(temp.toString());
        System.out.println(temp.getClass());

        return;
    }

    public static void main(String args[])
    {
        TestGeneric<Integer> tg = new TestGeneric<Integer>();
        tg.get("Crack!!!");
    }
}

а выход -

Crack!!!
class java.lang.String

Теперь имеет смысл? Объект - высший суперкласс. Таким образом, он может получить объект String из-за полиморфизма. Хотя вы производите тип или, скорее, я скажу, что сделать целочисленную опорную точку для строкового объекта, который Java знает внутри, это объект String во время выполнения. Было бы проблемой, если toString() не был определен в классе Integer. Вызываемая функция должна быть определена в ссылке, но реализация будет подобрана соответствующим образом в время выполнения из объекта .

Ответ 6

Если вы это сделаете, вам даже не нужно проверять ClassCastException, и он даже не сможет скомпилировать.

public class TestGeneric<T> {
    public void get (T arg){
        System.out.println(arg.toString());
    } 
}

TestGeneric<Integer> tg = new TestGeneric<Integer>();
tg.get("Crack!!!");

Ответ 7

Тип Integer имеет метод toString. Фактически каждый Object имеет этот метод, поэтому ClassCastException не будет встречаться.

Вы не назовете какой-либо String -специфический метод для вашего объекта, таким образом не произошло никакого исключения.

Причиной этого является то, что во время выполнения вы не увидите параметры типа type erasure.

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

Здесь возникает еще один вопрос, объясняющий исключение класса: объяснение

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

Таким образом, вы можете назвать это недостатком java по сравнению с С#.