作者chris译
或许大家java的多态问题对上溯下溯造型有了一定的概念对protected和private大家想必也很清楚但是这几个个结合在一起往往令人产生很多困惑在这里我举一个例子大家或许会发觉这篇文章对你来说还是很有意义的:
例子一共有两个class 可能出现困惑的地方我都会在后面一一解释A是父类B继承A并且实现了protectedTest(Object obj)方法如下面所示:
Bjava的源代码:
package matrixtest;
import matrixtestA;
/**
* <p>Title: protect private and upcasting </p>
* <p>Description: email:</p>
* <p>Copyright: Matrix Copyright (c) </p>
* <p>Company: </p>
* @author chris
* @version who use this example pls remain the declare
*/
public class B extends A
{
protected int protectedb = ;
protected int protectedab = ;
protected void protectedTest(Object obj)
{
Systemoutprintln("in BprotectedTest(Object):" + obj);
}
}
Ajava的源代码:
package matrixtest;
import matrixtestB;
/**
* <p>Title: protect private and upcasting </p>
* <p>Description: email:</p>
* <p>Copyright: Matrix Copyright (c) </p>
* <p>Company: </p>
* @author chris
* @version who use this example pls remain the declare
*/
public class A
{
protected int protecteda = ;
protected int protectedab = ;
private void privateTest()
{
Systemoutprintln("in AprivateTest()");
}
protected void protectedTest(Object obj)
{
Systemoutprintln("in AprotectedTest(Object):" + obj );
}
protected void protectedTest( String str )
{
Systemoutprintln("in AprotectedTest(String):" + str);
}
public static void main (String[] args)
{
// Test A
A a = new A();
aprivateTest();
// Test B
String helloStr = "Hello";
Object helloObj = helloStr;
B b = new B();
A a = b; // 这里发生了什么?困惑
b=a; //编译错误困惑
b privateTest(); //编译错误困惑
bprotectedTest(helloObj); //输出结果?困惑
bprotectedTest(helloStr); //编译错误困惑
aprotectedTest(helloObj); //输出结果? 困惑
aprotectedTest(helloStr); //输出结果?困惑 ?
}
}
下面我来逐个解释每一处困惑的地方:
困惑
这里其实就是子类自动上溯造型到父类A这里a其实是指向了一个B类型的对象 我们通常都可以这样作: A a=b 这样作的意思实际上就是让a指向了一个类型B的对象—在这里就是b了
在java里面关于跨类引用有两条规则应该记住:
如果a是类A的一个引用那么a可以指向类A的一个实例或者说指向类A的一个子类
如果a是接口A的一个引用那么a必须指向实现了接口A的一个类的实例
所以根据这两个规则我们就不难理解例子中的A a = b是什么意思了
困惑:
A a = b是可以的但是为什么b=a却是不行? 在这里我们依然可以套用上面的两条规则我们可以看到b是类B的一个引用a既不是类B的实例也不是类B的子类的实例所以直接b=a就出现了编译错误
如果确实需要进行这样的转化我们可以这样作:b=(B)a; 进行强制转化也就是下溯造型 在java里面上溯造型是自动进行的但是下溯造型却不是需要我们自己定义强制进行
困惑:
b privateTest();编译不通过? 这是很显然的你可以回顾一下private的定义: 私有域和方法只能被定义该域或方法的类访问所以在这里b不能访问A的方法privateTest()即使b是A的子类的实例请看下面的例子
public class A
{
private int two(int i) { return i; }
}
class Test extends A {
public static void main(String[] args) {
Systemoutprintln(Atwo());
}
}
Systemoutprintln(Atwo());这行编译出错显然因为private方法不能在这个类之外被访问而protected则不同我们回顾一下protected的定义:
被保护的域或方法只能被类本身类的子类和同一程序包中的类所访问
下面是一个错误使用protected的例子:
package matrixtest;
public class ProtectedTest {
protected void show() {
Systemoutprintln("I am in protected method");
}
}
import matrixtest*;
public class Test {
public static void main (String[] args) {
ProtectedTest obj = new ProtectedTest();
objshow();
}
}
因为访问权限问题你会得到show() has protected access in testProtectedTest的出错信息
困惑:
bprotectedTest(helloObj); 输出的是in BprotectedTest(Object):… 这到底是为什么呢?
为什么jvm能够确定是输出B的方法而不是A的方法? 这就和jvm的运行机制有关系了 我们上面提到了a是一个A类型的引用但是指向了一个B类型的实例
在这里如果jvm根据引用的类型在这里就是A 来定义调用哪个方法的话那么应该是调用A的protectedTest(helloObj)
然后实际上不是这样的因为jvm的动态编译能力jvm会在runtime来决定调用哪一个method而不是在compile time 也就是所谓的latebinding(runtime)和earlybinding(compiletime)
困惑:
bprotectedTest(helloStr); 这里为什么会出现编译错误? 他可以调用类B的protectedTest(Object obj)方法啊把helloStr上溯造型成一个object就行了啊或者上溯造型到A然后调用A的protectedTest(helloStr)方法啊
呵呵问题的根源就在于此了既然有两种选择jvm应该选择那一种?这种不确定性如果交给jvm来动态决定的话势必带来程序的不确定性虽然java在其他的一些地方也有类似的情形出现比如static变量的循环定义造成的不确定性但是在这里jvm还是在编译阶段就解决了这个问题
所以我们会在这一步遇到编译错误: reference to protectedTest is ambiguous; both method protectedTest(javalangString)
in mytestA and method protectedTest(javalangObject) in mytestB match at
line
在这里我们遇到的是显式的reference ambiguous错误但是有时候隐式的reference ambiguous却往往是更加的危险我举个例子:
父类的源代码:
public super
{
private void test(int i long j);
{
Systemoutprintln(i+and+j);
}
}
子类的源代码:
public sub
{
private void test(long j int i);
{
Systemoutprintln(i+and+j);
}
}
子类和父类都用有相同名称的方法test参数类型不同而已这种情况下编译可以被通过
但是如果你在另外一个类中用到了如下代码:
Sub sb = new Sub();
sbtest( );
你就会遇到编译错误因为没有确定的指出的类型所以造成reference ambiguous的错误了
困惑
aprotectedTest(helloObj);
输出结果分别是in Bp