2

我正在为 Kotlin 编写一个注释处理器,它需要知道如何调用方法,即是否需要构造一个类。

以下适用于 Java 代码、值和@JvmStatic-labeled 代码,但不适用于 Kotlinobject 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)

检测是否可以在不构造类的情况下调用方法的最佳方法是什么?

4

2 回答 2

1

这不是一个完美的解决方案,但这就是我想出的。它检查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
    }
}
于 2018-06-21T18:17:33.343 回答
0

所以在内部objectKotlin一个简单的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 创建类似的类,它的行为将是 a class(not object)。

public final class B {
    public static final B INSTANCE;

    public final void b() {
    }

    static {
        B var0 = new B();
        INSTANCE = var0;
    }
}

但是,当我们尝试像这样从 Java 创建一个新实例时,它会说它A具有私有构造函数并且无法创建。

    new A();
    new B();

@Metadata所以这里的差异是通过定义这种差异的 Kotlin注释产生的。

作为一种解决方案,您可以检查INSTANCE静态字段,也可以以某种方式读取 Kotlin 元数据 :)

于 2018-06-21T10:37:50.843 回答