我需要检查包中是否存在类。我知道我可以使用Class.forName
来检查类是否存在,但我如何验证它是否在特定的包中?
不要为此使用class.forName
Class.forName采用完全限定的名称。完全限定的名称包括包,但也包括外部类,因此,在这里不起作用:
package pkg;
class Outer {
class Inner {}
}
结果是完全限定的名称,即必须传递给CFN的名称,对于Inner
是:Class.forName("pkg.Outer.Inner");
-以及如何告诉Outer
是外部类而不是包名称的一部分?
Java不具有分层包;pkg
和pkg.subpkg
之间没有关系,所以您的问题希望不会涉及"如何检查包部分是否以特定字符串开始",因为您不应该在java生态系统中问这个问题。
因此,让我们离开Class.forName.
请注意,该类需要在运行时可用,否则将无法工作"幸运的是";,如果该类在运行时不可用,并且您希望确定给定的包(例如,一个完全限定的类名),由于外部和内部类的上述问题,这项工作实际上是不可能的,所以如果这就是您的问题的根源,您可以停止阅读:No can do。让我们假设在运行时可用。
您需要一个Class<?>
对象
每个类都由一个java.lang.Class<?>
类型的对象表示。你需要获得这样一个对象,然后你可以确定它在哪个包中。
策略1:Class.forName
Class.forName("pkg.Outer.Inner")
将为您带来课程<gt;对象,从那里你可以问它的包是什么,会得到pkg
,你可能想知道。因此,这是一种方法:给定一个表示类的完全限定名称的字符串,将其抛出Class.forName
,然后对由此得到的class对象进行操作。
策略2:课堂文字
Java有特殊的语法来获得给定类型引用的Class<?>
对象。所以,如果你在写代码时知道类型引用,你可以使用这个:
package pkg;
class Outer {
class Inner{}
private static Class<?> innerClassObj = Inner.class;
}
然而,如果您可以这样写,那么在编写时您就已经知道该类来自哪个包,所以这使得您的问题完全没有意义。为什么要在运行时找出你已经知道的东西?
万一这是你想知道的:检查你的导入,在任何主要的IDE中,按住CMD(非Mac上的CTRL),然后单击名称,它会带你到定义它的地方,包就会在那里列出。或者只是浮在上面,这在大多数IDE中也同样有效。
策略3:从对象实例
所有对象都有一个.getClass()
方法,该方法获得代表对象创建方式的Class<?>
实例。
不过要小心!
List<String> list = new ArrayList<String>() {
@Override public boolean add (String other) {
log.info("Added: {}", other);
return super.add(other);
}
};
这是非常有效的、有点常见的、完全无辜的java代码。然而,这意味着现在调用list.getClass()
,然后询问该类的名称,会得到类似com.foo.internal.WhateverClassThatCodeShowedUpIn$1
的东西,因为从技术上讲,这是一个子类,因此list
就是它的一个实例。如果您想检查对象是否是"来自java.util包的类",那么只看list.getClass()
就会错误地告诉您它不是。
解决方法是要意识到这一点,并始终(在while
循环中)遍历所有超类。list.getClass().getSuperclass()
将解析为与java.util.ArrayList.class
完全相同的实例,调用上的getSuperclass将获得java.util.AbstractList.class
,然后是java.lang.Object.class
和null
。java.util.List.class
从未出现在这里——那不是一个类,那是一个接口。如果你也想要这些,那么.getInterfaces()
就存在了。
所以,如果你想知道:这个对象与某个特定包中的某个类兼容吗?这就是你的答案。唯一的方法是使用while循环(若要检查接口,甚至可以使用队列或递归方法)。
策略4:把它给你
您总是可以有一个方法,将Class<?>
作为参数。各种各样的API也为您提供了一个。
好吧,我有一个Class<?>
实例,现在怎么办
您可以在上面调用.getPackage()
方法,但不幸的是,JVM规范规定这实际上不必返回一些东西(它可能返回null
)。所以这不是一个好的解决方案。相反,我建议您在它上调用.getName()
,然后使用您得到的字符串进城。
你得到的字符串应该是pkg.Outer$Inner
。您可以看到如何从以下内容中导出包:
- 查找最后一个
.
- 如果它存在,就把它和它之后的一切都去掉
- 如果根本没有点,它就在未命名的包中
沃伊拉。那就剩下pkg
了。
注意:考虑到策略3中所写的内容:为了满足您的需求,您可能必须递归地扫描超类和所有超接口。