error in overriding generic collections in Java

前端 未结 5 1822
隐瞒了意图╮
隐瞒了意图╮ 2021-01-28 19:29

When I try to override a method that takes a List, I get the following compile error.

Multiple markers at this

相关标签:
5条回答
  • 2021-01-28 19:38

    Basically your impression is incorrect and this is impossible. The JLS considers this specifically illegal.

    From 8.4.2:

    The signature of a method m1 is a subsignature of the signature of a method m2 if either:

    • m2 has the same signature as m1, or

    • the signature of m1 is the same as the erasure (§4.6) of the signature of m2.

    Two method signatures m1 and m2 are override-equivalent iff either m1 is a subsignature of m2 or m2 is a subsignature of m1.

    The emboldened bit is important because it doesn't say "the erasure of m1 is the same as the erasure of m2". What it actually does allow is this (and some more convoluted examples like it):

    class A {
        void process(List<String> list) {}
    }
    class B extends A {
        @Override
        void process(List list) {} // |List| is erasure of List<T>
    }
    

    Since the method signature of B.process is the erasure of A.process it is an override.

    According to 8.4.9, an example like in the OP could then be an overload because the signatures are not override-equivalent:

    If two methods of a class ... have the same name but signatures that are not override-equivalent, then the method name is said to be overloaded.

    Except that it's specifically a compile-time error (8.4.8.3):

    It is a compile-time error if a type declaration T has a member method m1 and there exists a method m2 declared in T or a supertype of T such that all of the following conditions hold:

    • m1 and m2 have the same name.

    • m2 is accessible from T.

    • The signature of m1 is not a subsignature (§8.4.2) of the signature of m2.

    • The signature of m1 or some method m1 overrides (directly or indirectly) has the same erasure as the signature of m2 or some method m2 overrides (directly or indirectly).

    These restrictions are necessary because generics are implemented via erasure. The rule above implies that methods declared in the same class with the same name must have different erasures. ...

    0 讨论(0)
  • 2021-01-28 19:47

    List<String> and List<Integer> are different types and getname(List<String> num) and getname(List<Integer> num) are methods with different signatures. So the second doesn't override the first. So child can not extends parent whit this method.

    0 讨论(0)
  • 2021-01-28 19:50

    The error you have pointed out is obviously because of the @Override annotation. When validating annotations (which I presume must happen at the very early stage of compiling process) the type erasure hasn't occurred. So the types List is not same as List and you get the warning. But even without that annotation I get an error

        name clash: getname(java.util.List<java.lang.Integer>) in child and getname(java.util.List<java.lang.String>) in parent have the    same erasure, yet neither overrides the other
    class child extends parent{`. 
    

    This error clearly says that they have the same erasure but still neither over rides. So Java in principle doesn't allow it. I can understand it will lead to many problems/confusions if allowed. For e.g. if someone makes a call new Child().get_name( new List<String> () ), it can't be resolved to the Child method which will break the concept of over riding. So it is correct that it is not allowed.

    0 讨论(0)
  • 2021-01-28 19:53

    The error message is pretty clear: it has the same erasure, but the types don't match, so it's not considered an override. A List<Integer> is not a List<String>; it can't treat it as either an override (which would require the types to match exactly) nor an overload (which would require the erasures to be different).

    0 讨论(0)
  • 2021-01-28 19:54

    To add to the answers already here, I want to comment about the signature of the methods is the same after erasure... but the compiler checks the method type before erasure.

    You could do something like creating a "different" Parent class, such as

    class Parent {
      public void getname(List<Integer> num) {
        System.out.printf("child class: %s",num);
      }
    }
    

    , compile it, use it to compile Child class, and then mix your original Parent.class and Child.class in the same JVM without issue, avoiding the compiler checks and using type erasure.

    But as long as the compiler notices doing something like that "in the same run", it will fail by the reasons explained by Ashot and Louis.

    0 讨论(0)
提交回复
热议问题