如何获取Java中引用的变量类型?

Posted

技术标签:

【中文标题】如何获取Java中引用的变量类型?【英文标题】:how to get type of variable referenced in Java? 【发布时间】:2013-09-17 12:55:13 【问题描述】:

我有以下代码。下面是两个问题:

class Parent   

        private void test()  
          
            System.out.print("test executed!");  
        
        static void print()
            System.out.println("my class is parent");
        


class Child extends Parent  
      
    static void print()
        System.out.println("my class is Child");
    

      


   public class Inheritence 

        public static void main(String[] args)  
          
            Child p1 = new Child();
            Parent p = new Child();  
            System.out.println("The class name of p is "+p.getClass());
            System.out.println("The class name of p1 is "+p1.getClass());
            System.out.println("p instance of child "+ (p instanceof Child));
            System.out.println("p1 instance of child "+ (p1 instanceof Child));
            //p.test(); 
            p.print();
             
      

输出是:

The class name of p is class Child
The class name of p1 is class Child
p instance of child true
p1 instance of child true
my class is parent

我认为p 的类名将是Parent,因为它的类型是Parent。但是,它打印为Child。那么我将如何获得ptype

这里的第二个问题是私有方法是否被继承。虽然包括this、cmets 在内的许多文章都认为私有方法没有被继承,但我在下面的示例中看到它是被继承的。可能是下面的一些类型转换问题。

class Child1 extends Parent1  
      

      


    public class Parent1   

        private void test()  
          
            System.out.print("test executed!");  
          

        public static void main(String[] args)  
          
                Parent1 p = new Child1();  
                p.test();
                Child1 c = new Child1();
                //c.test(); The method test from parent1 is not visible
                 
          

Output is : test executed!

在这里,我在 Child1 类型为 Parent1 的对象上调用 test 方法。 Child1 没有 test 方法,因为它不是继承的。但我仍然得到输出,这表明私有方法是继承的!如果test 是一个受保护的方法,并且我在子类中重写,那么尽管调用它的对象类型是parent(parent p1 = new child1());,但仍会执行被重写的方法。

编辑:经过几次 cmets,我将 Parent1 类和 Child1 类分开,并创建了一个名为 App 的新类,它构造了一个父对象和子对象。现在我不能在下面的代码中调用p.test

class Child1 extends Parent1  
      


      


     class Parent1   

        private void test()  
          
            System.out.print("test executed!");  
         

    public class App1

        public static void main(String[] args)  
          
            Parent1 p = new Child1();  
            p.test();//The method test from parent is not visible
            Child1 c = new Child1();
            //c.test(); //The method test from parent1 is not visible

             
      

【问题讨论】:

【参考方案1】:

解决第二个问题:将“继承”和“可见性”的概念区分开来可能会有所帮助。 private 方法 m 仅在声明该方法的类 C可见。所以你根本不能从外部C 使用m。此外,即使在C 内部,也不能使用x.m(),除非将对象x 声明为C 类型。但是子类的对象仍然有方法m,并且可以调用该方法,但只能在C内部调用。此示例将编译(即使两个类位于不同的文件中):

public class Class1 
    private void test ()  System.out.println ("test"); 
    public void doThis (Class2 c) 
        // c.test();  -- does not compile [1]
        ((Class1)c).test();
    


public class Class2 extends Class1 
    public void doSomething () 
        doThis (this);
        // ((Class1)this).test();  -- does not compile [2]
    

请注意,在doThis 内部,您仍然可以调用ctest 方法,即使c 的类型为Class2。但是,您只能这样做,因为代码位于声明 test() 方法的类 Class1 中(这就是 [2] 无法编译的原因);您只能通过将c 转换为Class1 类型的对象来做到这一点(这就是[1] 无法编译的原因)。然而,即使在你转换它以使编译器将其视为Class1 类型的表达式之后,实际对象仍然具有Class2 类型。如果你像这样调用polymorphic 的重写方法:((Class1)c).polymorphic(),它将调用Class2 中定义的方法,而不是Class1,因为该对象实际上仍然是Class2 对象。

所以我认为在某种意义上,test 继承的,即使它是私有的;它只是在 Class2 中不可见。

更多:我认为了解编译时类型和运行时类型(或实例类型)之间的区别也很有帮助。如果您声明变量Parent x;,那么x 的编译时类型为Parent。如果你有一个函数f,它的返回类型是Parent,那么像obj.f(arg,arg2) 这样的表达式将有Parent 类型。但是在运行时,如果变量或表达式具有编译时类型Parent,则运行时的实际类型可以是Parent或其任何子类。运行时类型将基于对象的构造方式。所以一个变量可以有一个编译时类型Parent和一个运行时类型Child。然后你只需要知道使用哪种类型以及何时使用。为了检查方法是否可见(这是private 发挥作用的地方),使用编译时类型。为了决定当子类覆盖方法时调用哪个方法,使用运行时类型(这就是多态性)。对于.getClass(),使用运行时类型。不管怎样,我就是这样想的,以免我太糊涂。

示例:假设我们有:

class Parent  
class Child extends Parent  
class Grandchild extens Child  

在其他类中,我们有

Parent x1 = new Parent();
Parent x2 = new Child();
Parent x3 = new Grandchild();

变量x1x2x3都有一个编译时类型 Parent。这意味着所有三个变量都可以引用一个实例 ParentChildGrandchild 或任何其他实例 Parent 的子类。这就是上面发生的事情:x2 将参考 指向Child 的实例,x3 将引用 Grandchild.

同样:

private Parent getAParent(int n) 
    if (n == 0) return new Parent();
    if (n == 1) return new Child();
    if (n == 2) return new Grandchild();
    throw new IllegalArgumentException();


Parent x4 = getAParent (0);
Parent x5 = getAParent (1);
Parent x6 = getAParent (2);

x5 指的是Child 的一个实例,x6 指的是一个实例 Grandchild.

但是所有变量的编译时类型和getAParent 电话仍然是Parent。编译器不知道哪个类 变量或函数调用实际上是指您运行程序时。 所以如果你在Grandchild 中声明一个方法play(),这些是 仍然是非法的:

x3.play ();             // ERROR
x6.play ();             // ERROR
getAParent(2).play ();  // ERROR

因为编译器会想到两个变量和getAParent(2) 具有类型Parent,而不是Grandchild。并且 play 没有定义 为Parent。要查看这些变量是否具有 run-time 类型 有一个play 方法需要编译器生成代码 在运行时检查,编译器不会这样做。

这就是p.test() 在您的第二个示例中起作用的原因。尽管pChild1的一个实例,p的编译时类型是 Parent1,因为您将其声明为具有 Parent1 类型。和 编译器看到Parent1 有一个test 方法;并且由于代码 在Parent1 内部,test 方法是可见的,即使它是 私人的。这就是它起作用的原因。编译器不会生成代码 检查运行时类型 p 实际指的是什么。希望这可以帮助 解释事情。

【讨论】:

If you have a function f whose return type is Parent, then an expression like obj.f(arg,arg2) will have type Parent. But at run-time, if a variable or expression has compile-time type Parent, the actual type at run-time can be Parent or any of its subclasses. The run-time type will be based on how the object was constructed. -- 这很令人困惑。你能举个例子吗? @user2708477 编辑了我的答案以包含一个示例。希望这会有所帮助,但我觉得这是我能做的最多的事情了。【参考方案2】:

我认为 p 的类名是 Parent,因为它是 Parent 类型。但是,它会打印为 Child。

类名将反映实例的类型,而不是变量的类型。

这里我正在调用 Parent1 类型的 Child1 对象的测试方法。 Child1 没有测试方法,因为它不是继承的。

但是测试方法继承可能是因为你的两个类在同一个文件中,所以私有方法对每个都是可见的,所以 Child1 将继承并因此具有可用其父级的所有非私有方法。这就是继承的全部意义——重用父级的行为(方法)和状态(字段)。 Child1 没有做的是它不覆盖任何父方法。


编辑

测试方法在父类中是私有的,所以继承不正确

它是可用的,因为您的代码似乎在同一个文件中。试着把你的类放在它们自己的文件中,看看会发生什么(看看代码是否能编译)。

【讨论】:

测试方法在父类中是私有的,所以它没有被继承对吗?有没有办法获取变量的类型。即虽然 p 是一个子对象,但它在声明时的类型是父对象。所以我在想是否可以检索有关 p 类型的信息。 @HovercraftFullOfEels:“看看代码是否会编译”(在他们自己的文件中使用类):我试过了。确实如此。 @ajb:不是上面的 OP 代码。或者你是在告诉我,当子类在不同的文件中时,他们可以通过调用父类的私有方法来逃避?? @HovercraftFullOfEels:实际上并不是 child 类调用了父类的私有方法。对p.test() 的调用在父类的主体中,对象的(静态)类型是父类,即使p 在运行时的实际类型是子类。 (如果你说的是c.test(),它被注释掉了,我没有取消注释。) @ajb:我对代码进行了一些编辑。现在代码甚至无法编译。我认为您是正确的,因为父类正文中的p.test() 有所不同。所以如果我被问到私有方法是否在子类中被继承,我的回答应该是它们被继承但不可见,对吧..?

以上是关于如何获取Java中引用的变量类型?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Java 泛型中获取类型变量的类

Webassembly如何获取f64的指针或引用

Java基础Java中如何获取一个类中泛型的实际类型

当copylocal属性为false时如何从引用dll中获取类型

如何在Java中获取给定类的数组类?

在java中如何判断一个变量的类型