我正在为Kotlin编写一个注释处理器,它需要知道如何调用一个方法,即是否需要构造一个类。
以下适用于Java代码,值和@JvmStatic
标记代码,但不适用于Kotlin object Foo {}
单例:
import javax.lang.model.element.Element
import javax.lang.model.element.Modifier
// Fails if el is in a singleton
fun isStatic(el: Element) = el.modifiers.contains(Modifier.STATIC)
检测方法是否可以在不构建类的情况下调用方法的最佳方法是什么?
答案 0 :(得分:0)
因此,object
中的内部Kotlin
是一个简单的Singleton
。区别在于它是由Kotlin语言本身管理的。仍然需要实例化此类以调用其功能。实例化是由Kotlin进行的,因为对象具有私有构造函数和包含其单个实例的静态INSTANCE
字段。
让我们看一下这个例子。我已经这样定义object A
:
object A {
fun a() {
}
}
如果我们看一下Java字节码并将其转换为Java,我们将得到:
import kotlin.Metadata;
@Metadata(
mv = {1, 1, 10},
bv = {1, 0, 2},
k = 1,
d1 = {"\u0000\u0012\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\n\u0002\u0010\u0002\n\u0000\bÆ\u0002\u0018\u00002\u00020\u0001B\u0007\b\u0002¢\u0006\u0002\u0010\u0002J\u0006\u0010\u0003\u001a\u00020\u0004¨\u0006\u0005"},
d2 = {"Lcom/telenor/self_service/app/A;", "", "()V", "a", "", "production sources for module app"}
)
public final class A {
public static final A INSTANCE;
public final void a() {
}
static {
A var0 = new A();
INSTANCE = var0;
}
}
如您所见,仅阅读 类主体,我们无法理解它是否为object
,因为如果我们仅使用Java创建类似的类,它将表现为{ {1}}(不是class
)。
object
但是,当我们尝试像这样从Java创建新实例时,它将说public final class B {
public static final B INSTANCE;
public final void b() {
}
static {
B var0 = new B();
INSTANCE = var0;
}
}
具有私有构造函数,无法创建。
A
因此,此处的差异是通过定义此差异的Kotlin new A();
new B();
注释产生的。
作为解决方案,您可以检查@Metadata
静态字段,也可以通过某种方式读取Kotlin元数据:)
答案 1 :(得分:0)
这不是一个完美的解决方案,但这就是我想出的。它会在二进制数据中检查kotlin.Metadata
,以查看它是否是Kotlin类,然后使用试探法来弄清它是否是静态的。
如果要查找主文件(即可以从命令行运行的文件),则需要类似的技术。
fun isKotlinClass(el: TypeElement)
= el.annotationMirrors.any { it.annotationType.toString() == "kotlin.Metadata" }
/** Check for Java static or Kotlin singleton.
* An imperfect heuristic: if not static, checks for a static INSTANCE field. */
private fun isStatic(element: Element): Boolean {
if (element.modifiers.contains(Modifier.STATIC)) return true
else {
val parent = element.enclosingElement
if (parent is TypeElement && isKotlinClass(parent)) {
val instances = parent.enclosedElements
.filter { "INSTANCE" == it.simpleName.toString() }
.filter { it.modifiers.contains(Modifier.STATIC) }
.filter { it.kind.isField }
return instances.isNotEmpty()
}
return false
}
}