Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/393.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java多态泛型调用_Java_Generics_Inheritance_Polymorphism - Fatal编程技术网

Java多态泛型调用

Java多态泛型调用,java,generics,inheritance,polymorphism,Java,Generics,Inheritance,Polymorphism,我最近刚开始在Java中处理泛型,遇到了一些奇怪的行为。这是简化版本,但我有一个基类,它由多个类扩展,这些类被传递到一个泛型函数中。在这个函数中,我调用了一个方法,该方法有多个版本以不同的基类或派生类作为参数 如果我有以下资料: public class A{}; public class B extends A{}; public class C extends A{}; public class Runner { public static void Run( ClassA a ){

我最近刚开始在Java中处理泛型,遇到了一些奇怪的行为。这是简化版本,但我有一个基类,它由多个类扩展,这些类被传递到一个泛型函数中。在这个函数中,我调用了一个方法,该方法有多个版本以不同的基类或派生类作为参数

如果我有以下资料:

public class A{};
public class B extends A{};
public class C extends A{};
public class Runner
{
    public static void Run( ClassA a ){Do Something};
    public static void Run( ClassB b ){Do Something};
    public static void Run( ClassC c ){Do Something};
}
void SomeRandomCall<B extends ClassA>( B b )
{
     Runner.Run( b );
}
SomeRandomCall<ClassB>( new ClassB() );
公共类A{};
公共类B扩展了A{};
公共类C扩展了{};
公开课跑者
{
公共静态无效运行(ClassA a){Do Something};
公共静态void Run(ClassB){Do Something};
公共静态void Run(ClassC){Do Something};
}
无效随机呼叫(B)
{
跑步者。跑步(b);
}
SomeRandomCall(新类b());
我在debug中发现
Runner.Run
调用的是
Run(ClassA)
,而不是Run
(ClassB)
函数。给定这两个函数,是否应该调用
Run(ClassB)
,因为提供了特定于类型的函数


如果不是这样的话,我怎么能有一个泛型函数来调用它呢?我怎么能调用具有签名的函数,这些签名采用基类和派生类呢

好的,由于
A
扩展了
B
,那么类型为
B
的对象可以作为参数在接受类型为
A
的参数的方法中传递。同样的情况也发生在这里。由于
run(A)
出现在
run(B)
之前,因此执行前者。因此,你的错误(如果你正在考虑的话)

为了进一步阐述,请考虑下面的例子:

    public static void main(String[] args) {
        A ab = new B();
        new Runner().Run( ab );
    }
程序员
是一个超类(类似于
A
)。
Java程序员
Programmer
的子类,而
C++程序员
也是
B
C
的子类

现在,有三个门,按距离增加的顺序:

  • 对于程序员来说
  • 面向Java程序员
  • 对于C++程序员,
    由于第一扇门离您最近,并且符合条件,因此您可以立即进入。你不想花更多的精力去第二扇门。这里也会发生同样的情况。

    既然
    A
    扩展了
    B
    ,那么
    B
    类型的对象可以作为参数传递给一个方法,该方法接受类型为
    A
    的参数。同样的情况也发生在这里。由于
    run(A)
    出现在
    run(B)
    之前,因此执行前者。因此,你的错误(如果你正在考虑的话)

    为了进一步阐述,请考虑下面的例子:

        public static void main(String[] args) {
            A ab = new B();
            new Runner().Run( ab );
        }
    
    程序员
    是一个超类(类似于
    A
    )。
    Java程序员
    Programmer
    的子类,而
    C++程序员
    也是
    B
    C
    的子类

    现在,有三个门,按距离增加的顺序:

  • 对于程序员来说
  • 面向Java程序员
  • 对于C++程序员,
    由于第一扇门离您最近,并且符合条件,因此您可以立即进入。你不想花更多的精力去第二扇门。这里也会发生同样的情况。

    因为你的缩写让我有点困惑,我做了一个小的、可运行的例子。我假设
    SomeRandomCall
    中的
    B
    是泛型类型,而不是类B

    这是:

    public class Main {
    
        public static class A{};
        public static class B extends A{};
        public static class C extends A{};
        public static class Runner{
            public static void Run( A a ){System.out.println("A");};
            public static void Run( B b ){System.out.println("B");};
            public static void Run( C c ){System.out.println("C");};
        }
    
        static <T extends A> void SomeRandomCall( T x ){
             Runner.Run( x );
        }
    
        public static void main(String[] args) {
            B b = new B();
            new Runner().Run( b );
        }
    }
    

    现在发生了什么?现在输出是A。原因是,在编译时,
    ab
    的类型是
    A
    ,因此我们只有一个候选方法可以执行:
    Run(aa)
    。运行时中实际的
    ab
    类型不再重要,因为我们只有一个候选者可以执行。无论
    ab
    A
    B
    还是
    C
    ,输出都是A。

    由于您的缩写对我来说有点混淆,我做了一个小的、可运行的示例。我假设
    SomeRandomCall
    中的
    B
    是泛型类型,而不是类B

    这是:

    public class Main {
    
        public static class A{};
        public static class B extends A{};
        public static class C extends A{};
        public static class Runner{
            public static void Run( A a ){System.out.println("A");};
            public static void Run( B b ){System.out.println("B");};
            public static void Run( C c ){System.out.println("C");};
        }
    
        static <T extends A> void SomeRandomCall( T x ){
             Runner.Run( x );
        }
    
        public static void main(String[] args) {
            B b = new B();
            new Runner().Run( b );
        }
    }
    
    现在发生了什么?现在输出是A。原因是,在编译时,
    ab
    的类型是
    A
    ,因此我们只有一个候选方法可以执行:
    Run(aa)
    。运行时中实际的
    ab
    类型不再重要,因为我们只有一个候选者可以执行。无论
    ab
    A
    B
    还是
    C
    ,输出都是A

    这就是为什么

    EnclosingClass.<ClassB>SomeRandomCall( new ClassB() );
    
    它的签名是
    void SomeRandomCall(A)
    ,它将始终调用
    Run(A)

    另请参见为什么编译时类型与重载方法有关


    编译器只会在可能的情况下选择正确的重载方法。例如,上述内容将反编译为

      public static void main(java.lang.String[]);
        Signature: ([Ljava/lang/String;)V
        Code:
           0: new           #29                 // class Main$B
           3: dup
           4: invokespecial #31                 // Method Main$B."<init>":()V
           7: astore_1
           8: aload_1
           9: invokestatic  #32                 // Method Main$Runner.Run:(LMain$B;)V
          12: aload_1
          13: astore_2
          14: aload_2
          15: invokestatic  #18                 // Method Main$Runner.Run:(LMain$A;)V
          18: return
    
    publicstaticvoidmain(java.lang.String[]);
    签名:([Ljava/lang/String;)V
    代码:
    0:new#29//class Main$B
    3:dup
    4:invokespecial#31//methodmain$B.“”:()V
    7:astore_1
    8:aload_1
    9:invokestatic#32//方法Main$Runner.Run:(LMain$B;)V
    12:aload_1
    13:astore_2
    14:aload_2
    15:invokestatic#18//方法Main$Runner.Run:(LMain$A;)V
    18:返回
    
    每次调用
    Run
    都将使用不同的重载版本(由
    invokestatic
    后面的数字标识)

    这就是为什么

    EnclosingClass.<ClassB>SomeRandomCall( new ClassB() );
    
    它的签名是
    void SomeRandomCall(A)
    ,它将始终调用
    Run(A)

    另请参见为什么编译时类型与重载方法有关


    编译器将只选择正确的重载方法(如果可以)。例如,上面的方法将反编译为

      public static void main(java.lang.String[]);
        Signature: ([Ljava/lang/String;)V
        Code:
           0: new           #29                 // class Main$B
           3: dup
           4: invokespecial #31                 // Method Main$B."<init>":()V
           7: astore_1
           8: aload_1
           9: invokestatic  #32                 // Method Main$Runner.Run:(LMain$B;)V
          12: aload_1
          13: astore_2
          14: aload_2
          15: invokestatic  #18                 // Method Main$Runner.Run:(LMain$A;)V
          18: return
    
    publicstaticvoidmain(java.lang.String[]);
    签名:([Ljava/lang/String;)V
    代码:
    0:new#29//class Main$B
    3:dup
    4:特别是#31