Тып бяспекі генеріков ў Java

Я выявіў паводзіны адноўленых ў Java, што я зусім не магу зразумець (з маёй .NET фонам).

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

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

        return;
    }
}

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

Скажыце, калі ласка, чаму я не атрымліваю ClassCastException ў ГЭТ, акрамя таго, у ідэі, я бачу, як тэмп Радок пасля прысваення і тое, якое мае значэнне «Crack !!!» . Акрамя таго, як я мог, што ClassCastException закінуў? Я выкарыстоўваю JDK 1.7.0_07 на Windows 7 x64.

10

7 адказы

The reason you are not getting a class cast exception is that Java generics are implemented through type erasure. Unlike .NET generics that required significant changes to CLS, Java generics are processed entirely in compile-time. At runtime, the cast to T is ignored. In order to check type at runtime, you need to store Class, and use its methods to check the type of a parameter passed in:

public class TestGeneric
{
    private Class genClass;
    public TestGeneric(Class 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 tg = new TestGeneric(Integer.class);
tg.get("Bang!!!");//Now that's a real Bang!!!
14
дададзена
Вы сапраўды «патрэбныя» або ёсць лепш «лепшая практыка»? Мне падабаецца падыход nitegazer2003 згадвалася, , так што ясна, задэклараваных/бачнай/кароткім і не загрувашчваць цела кода. Яна таксама цесна паралелі, як джынэрыкі можа быць абмежаванае ў C #.
дададзена аўтар Jon Coombs, крыніца
хоць вы правільна адказалі на сваё пытанне, але ёсць пытанне аб тым, як выкарыстоўваць агульныя правільна і чаму вам трэба «вяртанне» у канцы нічога? і выглядае як спосабы больш складанымі, чым павінны быць. Я дадаў мой адказ пра тое, як вы павінны выкарыстоўваць агульны.
дададзена аўтар Jaxox, крыніца

Гэта адбываецца таму, што агульны тып Т мае пэўную мяжу, так яно разглядаецца як аб'ект. Кастынг то T не будзе выклікаць ClassCastException у гэтым выпадку.

However, if your class definition was public class TestGeneric, then you would get the ClassCastException if you passed in a String into get().

7
дададзена
+1 для адказу на пытанне OP яшчэ пра тое, як на самой справе атрымаць ClassCastException.
дададзена аўтар Jon Coombs, крыніца

Гэта павучальнае практыкаванне думаць пра тое, што не-універсальны код, што агульны код «сціраецца ў» выглядае наступным чынам:

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?
6
дададзена
Дзіўны адказ! Канкрэтны прыклад таго, што на самой справе робіць сціранне.
дададзена аўтар Jon Coombs, крыніца

Заўсёды памятайце Абагульненні ў Java з'яўляюцца кампіляцыі сутнасці часу. Гэта не мае нічога агульнага падчас выканання. Дазвольце мне дэма-код oqn вам.

public class TestGeneric
{
    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 tg = new TestGeneric();
        tg.get("Crack!!!");
    }
}

і выснова

Crack!!!
class java.lang.String

Makes sense now? Object is the supreme super class. So it can get a String object due to polymorphism. Though you are type casting or rather i will say making an integer reference point to a string object Java know internally it is a String object at runtime. There would have been a problem if toString() was not defined in Integer class. The function you call must be defined in the reference but the implementation will be picked up appropriately at runtime from the referenced object.

3
дададзена

Гэта адбываецца з-за тыпу сціранне . Гэта азначае, што ў Java ўсе джынэрыкі зводзіцца да Object падчас выканання. Такім чынам, вы адлітыя свой Радок у Object , які з'яўляецца цалкам нармальна. А так як ToString не рэалізуецца на Object , зноў не выключэнне.

Here is a link on type-erasure

The only way to really get the ClassCastException is to pass an instance of Class to the generic type and then do myClass.isInstance(arg) and throw an exception if false

3
дададзена
Што я маю на ўвазе «адзіны шлях» з'яўляецца тое, што агульнае не мае пэўнага тыпу падчас выканання. Таму, калі жаданае паводзіны з'яўляецца кінуць ClassCastException, тып павінен быць відавочна прадастаўлены радавым шляхам прапускання ў экзэмпляры Class , які затым можа быць выкарыстаны для праверкі тыпу. Ваш каментар пра Нумар дзейнічае толькі тады, калі гэта ўмова. Там няма нічога агульнага ажыццяўлення, можна меркаваць, што ён павінен працаваць толькі для асобнікаў Нумар .
дададзена аўтар John B, крыніца
Ці з'яўляецца гэта на самай справе «адзіны шлях»? nitegazer2003 згадаў, што дзівіць мяне ў якасці ачышчальнага падыходу, пашырае нумар, так што ясна, задэклараваны/бачнай/кароткай і не загрувашчваць цела кода. Але +1 для «у Java ўсе джынэрыкі зводзіцца да аб'екта».
дададзена аўтар Jon Coombs, крыніца

Калі вы робіце гэта такім чынам, вы нават не трэба, каб праверыць ClassCastException і ён не зможа нават скампіляваць.

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

TestGeneric tg = new TestGeneric();
tg.get("Crack!!!");
3
дададзена

Тып Цэлы лік мае метад ToString . На самай справе кожны Object мае гэты метад так ClassCastException не будзе адбывацца.

Вы не выклікаць любы Радок -специфический метад вашага аб'екта, такім чынам, выключэнне не адбылося.

Прычына гэтага заключаецца ў тым, што падчас выканання вы не ўбачыце параметры тыпу Beause з explanation

У гэтым кодзе вы можаце ўбачыць, што карыстальнік спрабаваў кінуць відавочна Радок ня радавога параметру.

Такім чынам, вы можаце назваць гэта недахопам Java ў параўнанні з C #.

2
дададзена
Я не казаў, што яна выклікаецца аўтаматычна.
дададзена аўтар Adam Arold, крыніца
«Вы не выклікаць любы метад String, спецыфічныя для аб'екта» Але на самай справе, гэта не ўяўляецца магчымым выклікаць любы метад String, спецыфічныя па гэтай спасылцы, так як ён мае тып T , параметр тыпу які можа быць любым тыпам спасылкі.
дададзена аўтар newacct, крыніца
-1: ToString ня выклікаецца аўтаматычна, калі вы прыводзіце нешта, і гэты кідок у іншым кірунку ў любым выпадку.
дададзена аўтар Don Roby, крыніца
Пасля рэдагавання ваш адказ, здаецца, не спасылацца ToString на ўсё, што неабходна, так як гэта не мае значэння. Калі б я на самой справе downvoted, я б ўцягваць яго зараз, але гэта, здаецца мой downvote не бралі.
дададзена аўтар Don Roby, крыніца