Java 泛型方法与多态,这样解释对吗 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
amiwrong123
V2EX    Java

Java 泛型方法与多态,这样解释对吗

  •  
  •   amiwrong123 2019-09-07 16:33:04 +08:00 6418 次点击
    这是一个创建于 2235 天前的主题,其中的信息可能已经有所发展或是发生改变。
    class A{} class B extends A{} class GenericMethods { public <T> T f1(T x) { System.out.println(x.getClass().getName()); return x; } } public class testP { public static void main(Sting[] args) { GenericMethods gm = new GenericMethods(); A a = gm.f1(new B()); //B b = gm.f1(new A());编译报错 } } 

    f1 方法是一个泛型方法,返回值是< >里的T,形参也是< >里的T。所以二者推断出来的具体类型必须一样,或者符合多态。

    执行A a = gm.f1(new B())时,返回值处的T被推断为A,形参处的T本来会被推断为B,但是由于前者,形参处的T这里被推断为A。这里传B对象作为实参符合多态,泛型和多态不冲突。

    还有就是 B b = gm.f1(new A());编译通不过怎么解释比较好,可以认为这句是推断为B返回的也是B只是这里不允许向下转型吗?

    第 1 条附言    2019-09-07 17:08:31 +08:00
    想问问 f1 这个泛型方法,返回值里的 T,形参里的 T。这两个地方的类型参数推断是分别进行的吗?

    我认为是分别进行的。形参里的 T 是靠实参推断的;返回值里的 T,是靠函数赋值过去的那个对象推断的。
    第 2 条附言    2019-09-08 12:17:55 +08:00
    ```java
    interface Generator<T> {
    T next();
    }

    public class BasicGenerator<T> implements Generator<T> {

    private Class<T> type;
    public BasicGenerator(Class<T> type) {
    this.type = type;
    }

    @Override
    public T next() {
    try {
    return type.newInstance();
    } catch (InstantiationException | IllegalAccessException e) {
    throw new RuntimeException(e);
    }
    }

    public static <T> Generator<T> create(Class<T> type) {
    return new BasicGenerator<T>(type);
    }

    public <T> Generator<T> create1(Class<T> type) {
    return new BasicGenerator<T>(type);
    }

    public <T> void test (T t) {
    System.out.println(t.getClass().getName());
    }

    public static void main(String[] args) {
    Generator<Integer> gen = BasicGenerator.create(Integer.class);
    for (int i = 0; i < 5; i++) {
    System.out.println(gen.next());
    }

    //gen.creat1(String.class); 以下两行编译报错
    //gen.test(1);
    }
    }
    ```
    各位,不好意思,又有个问题。这是我对上面的解释:
    create 方法是静态的泛型方法,而泛型类 BasicGenerator 的类型参数是与对象相关的,所以 create 方法里的 T 不是泛型类的 T。
    而 create1 方法是成员的泛型方法,因为对象 gen 已经被创建出来了,所以 create 方法里的 T 就是泛型类 BasicGenerator 的 T。也就是说,T 是 Inteter,现在所有的成员方法你都要把 T 看做 Integer 了。所以那两行会报错(是不是可以理解:使用同一个类型参数的情况下,只有在非泛型类里的成员泛型方法才能体现出泛型方法的灵活性,即泛型方法能够独立于类而产生变化)。报错信息如下:
    Error:(39, 12) java: 找不到符号
    符号: 方法 creat1(java.lang.Class<java.lang.String>)
    位置: 类型为 Generator<java.lang.Integer>的变量 gen
    Error:(40, 12) java: 找不到符号
    符号: 方法 test(int)
    位置: 类型为 Generator<java.lang.Integer>的变量 gen
    28 条回复    2019-09-26 13:44:01 +08:00
    hantsy
        1
    hantsy  
       2019-09-07 16:39:03 +08:00
    牛马不相及的两个东西。
    Raymon111111
        2
    Raymon111111  
       2019-09-07 16:42:55 +08:00
    B 是一个 A

    而 A 却不是一个 B

    换个简单的说法, 有一个装水果的篮子可以装苹果, 但是有一个装苹果的篮子肯定不能装水果, 因为这个水果可能是香蕉.
    Bromine0x23
        3
    Bromine0x23  
       2019-09-07 16:44:00 +08:00
    类型参数只能从方法参数推导的
    `gm.f1(new B())` 就推导成 `B f1(B)`,`gm.f1(new A())` 就推导成 `A f1(A)`
    amiwrong123
        4
    amiwrong123  
    OP
       2019-09-07 16:53:31 +08:00
    @Bromine0x23
    可是我觉得类型参数也能通过返回值处的类型参数推断啊。比如:
    ```java
    class GenericMethods {
    public <T> T f2(Object ) {
    System.out.println(x.getClass().getName());
    return (T)x;
    }
    }

    public class testP {
    public static void main(String[] args) {
    GenericMethods gm = new GenericMethods();
    int o1 = gm.f2(2);
    String o2 = gm.f2(2);//能通过编译,但运行时报错
    }
    }
    ```
    这里可以认为,执行`String o2 = gm.f2(2)`后,由于这里`T`就被推断为了`String`,一个实际为 2 的 Object 对象在向上转型为`String`类型后,执行(String)x,这句会受到 RTTI 的检查,被发现无法转型后便报错。
    Bromine0x23
        5
    Bromine0x23  
       2019-09-07 17:31:03 +08:00   1
    @amiwrong123
    详细说的话是 f2 的 T 参数在信息不足的情况下被推延了。
    这个推导是分段进行的,首先是对 gm.f2(2),由于 T 没出现在参数中所以这里不能确定(或者说可选范围是 <= Object )
    然后 int o1 = <T> 或者 String o2 = <T>,在就能确定 T 的类型了

    对前面 B b = gm.f1(new A()), 在 gm.f1(new A()) 中已经能确定 T = A 了,所以之后 B b = <A> 的表达式由于类型不匹配导致编译失败
    amiwrong123
        6
    amiwrong123  
    OP
       2019-09-07 17:38:33 +08:00
    @Bromine0x23
    谢谢回答。但 A a = gm.f1(new B());这里该怎么理解呢,照你这么说,意思就是,有了方法参数的推断,就不需要返回值的推断了。那这里 f1 的 T 就被推断为 B 了呗。

    只是 gm.f1(new B())返回了一个 B 对象,然后由于赋值,向上转型为了一个 A 对象。
    Bromine0x23
        7
    Bromine0x23  
       2019-09-07 17:40:31 +08:00
    @amiwrong123 是的
    cigarzh
        8
    cigarzh  
       2019-09-07 19:42:12 +08:00 via iPhone
    你这报错和泛型有啥关系……
    amiwrong123
        9
    amiwrong123  
    OP
       2019-09-07 19:59:42 +08:00 via Android
    @cigarzh
    主要类型参数推断理解错了
    axlecho
        10
    axlecho  
       2019-09-07 21:08:01 +08:00 via Android
    A a = new B ok
    B b = new A failed
    这里跟泛型没关系
    fengpan567
        11
    fengpan567  
       2019-09-07 21:17:00 +08:00
    和泛型没关系。这个是 B 是子类,A 是父类,父类的实例引用不能指向子类,但是子类实例引用是可以指向父类的
    ninjachen
        12
    ninjachen  
       2019-09-07 21:52:24 +08:00 via Android
    乡下转型。。。
    这个是什么词汇?
    只听过强行 cast,向下是不可能自动做的
    ninjachen
        13
    ninjachen  
       2019-09-07 21:52:45 +08:00 via Android
    乡下 typo,是向下
    amiwrong123
        14
    amiwrong123  
    OP
       2019-09-07 23:02:45 +08:00 via Android
    @ninjachen
    不好意思,创造了个新词汇 rz
    jxie0755
        15
    jxie0755  
       2019-09-08 09:01:16 +08:00
    泛型和多态好像没有什么很相关的地方? 虽然都是面向对象编程里的概念
    泛型是针对容器做出的设定
    多态是针对继承关系做出的设定
    如果你一定要对几个容器之间做继承关系的话.........那你参考下 Collection 和 Arraylist 之间是怎么个安排的吧?
    lolizeppelin
        16
    lolizeppelin  
       2019-09-08 10:57:58 +08:00
    你先用 python 之类的动态语言写一遍

    再用 java 这样的静态语言写一遍

    然后你就理解为什么静态语言需要泛型这玩意了

    不要死脑筋去理解,功能做出来是为了解决痛点的,你知道为什么痛了,自己然就懂了
    amiwrong123
        17
    amiwrong123  
    OP
       2019-09-08 12:20:55 +08:00
    @Bromine0x23
    大佬,能否再帮忙看下附言 2 的疑问,感谢!
    是不是成员泛型方法的 T 被泛型类的 T 覆盖掉了。
    itechify
        18
    itechify  
    PRO
       2019-09-08 12:46:14 +08:00 via Android
    我写了个博客,https://liuzhicong.cn/index.php/study/extends-T-and-super-T.html,
    想看精简版本,看借鉴参考的第二篇 stackoverflow 的就行了,私认为解释的很清楚
    amiwrong123
        19
    amiwrong123  
    OP
       2019-09-08 13:25:24 +08:00
    @oneisall8955
    是通配符方面的知识哈,等我看书看到这块再去观摩你的博客把。
    话说层主能否帮看下附言 2,不知道我的理解对吗。。
    Bromine0x23
        20
    Bromine0x23  
       2019-09-08 13:29:44 +08:00   1
    @amiwrong123
    你声明的 gen 是 Generator 而不是 BasicGenerator,当然调用不了 create1 和 test
    amiwrong123
        21
    amiwrong123  
    OP
       2019-09-08 13:55:30 +08:00
    @Bromine0x23
    不好意思,刚才没注意到那个静态方法的返回值。修改静态方法后可执行。

    经过测试发现,虽然成员的泛型方法和泛型类用了同一个标识符 T,但是成员泛型方法的 T 并没有被对象的具体类型 CountedObject 所覆盖,而是独立于对象的。
    guyeu
        22
    guyeu  
       2019-09-08 18:35:08 +08:00
    向下转型在 Java 里是不存在的,只能把一个对象转为对象真正的类型或者它的派生类。
    你可以把泛型理解为一个语法糖,这个语法糖的作用只是做一种类型提示,告诉编译器这个地方可能是什么类型,帮助用户和编译器做类型推断来检查一些错误。
    Justin13
        23
    Justin13  
       2019-09-08 19:39:24 +08:00 via Android
    我的理解是泛型就是多态的一种,泛型函数更关注数据结构(接口)而非具体的数据类型。
    houOne
        24
    houOne  
       2019-09-25 18:45:20 +08:00
    @guyeu #22 "只能把一个对象转为对象真正的类型或者它的派生类"
    这个派生类是说反了吗?

    class B extend A
    B 是 A 的派生类,A 是 B 的基类,B 由 A 派生出来

    只能把一个对象转为对象真正的类型或者它的基类?
    guyeu
        25
    guyeu  
       2019-09-25 20:17:57 +08:00
    @godloveplay #24 对对对,应该是基类。
    guyeu
        26
    guyeu  
       2019-09-25 20:19:02 +08:00
    @godloveplay #24 对象的类型并不会发生改变,改变的是引用的类型。
    houOne
        27
    houOne  
       2019-09-25 23:26:23 +08:00
    @guyeu #26 看得我陷入转牛角尖的状态。。
    有个疑问,那这个强转背后到底做了哪些事情,怎么实现的。- -
    ```java Object o = new A(); A a = (A) o```
    guyeu
        28
    guyeu  
       2019-09-26 13:44:01 +08:00
    @godloveplay #27 这个对象本来就是 A,强转也不过是是检查一下对象的类型
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     952 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 26ms UTC 20:38 PVG 04:38 LAX 13:38 JFK 16:38
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86