如何获取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
。那么我将如何获得p
的type
。
这里的第二个问题是私有方法是否被继承。虽然包括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
内部,您仍然可以调用c
的test
方法,即使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();
变量x1
、x2
和x3
都有一个编译时类型
Parent
。这意味着所有三个变量都可以引用一个实例
Parent
或 Child
或 Grandchild
或任何其他实例
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()
在您的第二个示例中起作用的原因。尽管p
指Child1
的一个实例,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中引用的变量类型?的主要内容,如果未能解决你的问题,请参考以下文章