假设我有这些课程:
public class ChildClass extends ParentClass
{
// some class definition here
}
public abstract class ParentClass
{
public static void printClass()
{
// get the class that extends this one (and for example, print it)
}
// some class definition here
}
假设在调用
ParentClass.printClass()
时我想打印班级的名称(就像做System.out.println(ParentClass.class)
一样)。然后扩展 ParentClass
(例如像 ChildClass
中那样)并调用 ChildClass.printClass()
时,我希望它打印扩展类的名称(就像做 System.out.println(ChildClass.class)
一样)。这有可能吗?
我找到了一种使用
MethodHandles.lookup().lookupClass()
从静态方法内部获取类的方法,但是当在 ParentClass.printClass
内部使用它并扩展 ParentClass,然后在扩展类上调用 printClass
时,我总是得到该类ParentClass
。
静态方法最好被认为完全存在于类本身之外。它们出现在类中的原因是 java(语言)本身的设计:类型不仅仅是具有层次结构的类型,它们还充当 java 命名空间系统的主要工具。
类型存在于包中,包是类型的顶级命名空间概念。那么如何引用一个方法呢?只有一种方法:通过类型系统。因此,静态方法必须放置在类型内部。但这就是结束的地方。
他们根本不继承。当你写下:
ChildClass.lookupClass()
编译器只是弄清楚:对,好吧,你显然指的是
lookupClass()
中的 ParentClass
方法,所以 that 就是我将编译的。您可以通过运行 javap -c -p MyExample
来亲自查看此操作。同样的原则甚至适用于非静态方法。
对于 instance 方法,runtime 会取消此操作:每当您在任何对象上调用方法时,运行时系统将始终执行动态调度;你不能选择退出。你可以写:
AbstractList<String> list = new ArrayList<String>();
list.sort(someComparator);
并且您可以使用
javap
来验证这最终是否会写入调用方法 AbstractList::sort 的类文件中。但是,在运行时,JVM 将始终检查 list
实际指向的内容 - 它是 ArrayList 的实例,而不是 AbstractList(很明显:AbstractList 是抽象的;没有对象可以直接实例化为“new AbstractList”)。如果 ArrayList 有自己的排序方法,那么 that 将被调用。
所有这些的关键要点是:静态方法不继承,因此,这个动态调度系统对它们不可用,因此,你想要的不能以这种方式完成。
感觉您正在做的事情是尝试将层次结构与适用于类本身的属性相关联。换句话说,您希望在 'ParentClass 的
lookupClass
方法和 ChildClass 的 lookupClass
方法的概念之间存在层次关系 - LookupClass 不是您询问 ChildClass 或 ParentClass 实例的东西 - 您在概念上询问它这些类型本身。
如果你想一下,构造函数也是一样的。您不会向 ArrayList 实例“询问”新的数组列表。你问ArrayList,这个概念。两者都“没有真正执行”继承,并且不能抽象为类型层次结构。
这就是工厂类发挥作用的地方。
工厂类作为一个概念只是“分层化”静态性,通过从中删除
static
:为类层次结构创建一个同级类型(例如ParentClassFactory
):
abstract class ParentClassFactory {
abstract ParentClass create();
abstract void printClass();
}
然后,在写
ChildClass
的同时,你也写 ChildClassFactory
。一般来说,工厂只有一个实例 - 您可能希望为此采用单例模式。现在你可以做得很好了:
class ChildClassFactory extends ParentClassFactory {
private static final ChildClassFactory INSTANCE = new ChildClassFactory();
public static ChildClassFactory instance() { return INSTANCE; }
public ParentClass create() { return new ChildClass(); }
public void printClass() { System.out.println(ChildClass.class); }
}
// elsewhere:
// actually gets the ChildClassFactory singleton:
ParentClassFactory factory = ....;
factory.printClass(); // will print ChildClass!
引用@RealSkeptic:
静态方法不被继承。事实上,您可以调用 ChildClass.printClass() 只是语法糖。它实际上总是调用 ParentClass.printClass()。所以你不能用静态方法做类似的事情,只能用可继承的非静态方法。
不可能?这是一个实现。
public abstract class ParentClass {
protected static void printClass()
{
// get the class that extends this one (and for example, print it)
Throwable t = new Throwable().fillInStackTrace();
StackTraceElement[] e = t.getStackTrace();
Class<?> childClazz = null;
for(int i=0; i<e.length; i++) {
Class<?> clazz;
try {
clazz = Class.forName(e[e.length-1-i].getClassName());
} catch (ClassNotFoundException e1) {
// shouldn't happen
throw new RuntimeException(e1);
}
if(ParentClass.class.isAssignableFrom(clazz)) {
childClazz = clazz;
break;
}
}
System.out.println(childClazz);
}
}
这里有一些例子给出了我期望的结果。
public class ChildClass extends ParentClass {
public void itWorksHereToo() {
printClass();
}
public static void main(String[] args) {
printClass();
new ChildClass().itWorksHereToo();
}
}
public class ChildsChildClass extends ChildClass{
public void whatAboutHere() {
printClass();
}
public void thisToo() {
this.itWorksHereToo();
}
public static void main(String[] args) {
printClass();
new ChildsChildClass().whatAboutHere();
new ChildsChildClass().thisToo();;
}
}
public class NotAChild {
public void thisWorks() {
new ChildsChildClass().thisToo();
}
public static void main(String[] args) {
ChildsChildClass.main(args);
new ChildClass().itWorksHereToo();
new NotAChild().thisWorks();
}
}