在kapt中检测单身人物

时间:2018-03-28 01:28:38

标签: kotlin kapt

我正在为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)

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

2 个答案:

答案 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
    }
}