Interface关键字的一个重要目标就是允许程序员隔离构件,降低耦合性。但是通过类型信息,这种耦合性还是会传递出去,见例子:
1. 定义接口A:
package reflect;
public interface A {
void f();
}
2. 实现类B:
package reflect;
public class B implements A {
@Override
public void f() {
System.out.println("public B.f()");
}
public void g() {
System.out.println("public B.g()");
}
}
3. 客户端代码:
public class InterfaceViolation {
public static void main(String[] args) {
A a = new B();
System.out.println(a.getClass().getName());
if(a instanceof B) { //客户端程序员可向下转型为指定类型
B b = (B)a;
b.g();
}
}
}
这种处理是完全合法的,但是增加了代码耦合性。一种解决办法对实现使用包访问权限
4. 实现类C, 限制访问权限:
package reflect;
public class HiddenC {
public static A makeA() {
return new C();
}
}
class C implements A {
@Override
public void f() {
System.out.println("public C.f()");
}
public void g() {
System.out.println("public C.g()");
}
void u() {
System.out.println("package C.u()");
}
protected void i() {
System.out.println("protected C.i()");
}
private void h() {
System.out.println("private C.h()");
}
}
5. 受限的客户端代码(在C类的包外部,访问是受限的):
a) 此时仍可通过暴力反射来访问C类的所有方法
package designpattern;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import reflect.A;
import reflect.HiddenC;
public class HiddenImplementation {
public static void main(String[] args) {
A a = HiddenC.makeA();
System.out.println(a.getClass().getName());
a.f();
//C类位于reflect包,且访问权限为包权限,故此处无法调用
// if(a instanceof C) {
// C c = (C)a;
// c.g();
// }
callHiddenMethod(a, "g"); //反射调用不可见的方法
callHiddenMethod(a, "u");
callHiddenMethod(a, "i");
callHiddenMethod(a, "h");
}
static void callHiddenMethod(Object o, String methodName) {
try {
Method m = o.getClass().getDeclaredMethod(methodName);
m.setAccessible(true); //暴力反射
m.invoke(o);
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}