改进的数组内联函数

时间:2018-09-26 21:31:05

标签: kotlin

在Kotlin中是否可以编写具有可返回不同类型Arrays的类型化的内联函数?我在想这样的事情:

inline fun <reified E> getArray(key: String, defValue: Array<E>): Array<E>? {
    return when(defValue) {
        is Array<Int> -> // ...
        is Array<String?> -> // ...
        else // ...
    }
}

我想这样称呼它:

fun intArray(size: Int): Array<Int> = Array(size) {i -> 0}
fun stringArray(size: Int): Array<String?> = Array(size) {i -> null}

val strings: Array<Int> = getArray(KEY_INTS, intArray(0))
val strings: Array<String> = getArray(KEY_STRINGS, stringArray(0))

但是,我得到了错误:

  

找不到删除类型实例的支票

1 个答案:

答案 0 :(得分:4)

明确回答问题-您可以通过检查E类来使用它:

inline fun <reified E: Any> getArrayInline(key: String, defValue: Array<E>): Array<E>? {
    return when(E::class) {
        Int::class -> arrayOf(1, 2, 3)
        String::class -> arrayOf("a", "b", "c")
        else -> throw IllegalArgumentException("Invalid class: ${E::class.qualifiedName}")
    } as Array<E>
}

但是我不鼓励使用它,因为:

  1. 类型不安全-您必须对结果执行不安全的转换,即使when用例中不包含任何数组类型,也都可以对其进行调用
  2. 它是内联的-因此,每当您使用该方法时,整个代码块都会复制到字节码中(见下文)
  3. 类型检查在运行时完成,因此会损害性能

使用时会发生什么?让我们检查一下这个例子:

fun testArrayInline(){
    val test = getArrayInline("key", emptyArray<Int>())
    val test2 = getArrayInline("key2", emptyArray<String>())
}

简单吧?但是一旦您查看了生成的字节码,它就不太好了。出于可读性考虑,这是将Kotlin字节码反编译回Java:

public static final void testArrayInline() {
  String var1 = "key";
  Object[] defValue$iv = new Integer[0];
  KClass var3 = Reflection.getOrCreateKotlinClass(Integer.class);
  Object var10000;
  if (Intrinsics.areEqual(var3, Reflection.getOrCreateKotlinClass(Integer.TYPE))) {
     var10000 = new Integer[]{1, 2, 3};
  } else {
     if (!Intrinsics.areEqual(var3, Reflection.getOrCreateKotlinClass(String.class))) {
        throw (Throwable)(new IllegalArgumentException("Invalid class: " + Reflection.getOrCreateKotlinClass(Integer.class).getQualifiedName()));
     }

     var10000 = new String[]{"a", "b", "c"};
  }

  Integer[] test = (Integer[])((Object[])var10000);
  String var7 = "key2";
  Object[] defValue$iv = new String[0];
  KClass var4 = Reflection.getOrCreateKotlinClass(String.class);
  if (Intrinsics.areEqual(var4, Reflection.getOrCreateKotlinClass(Integer.TYPE))) {
     var10000 = new Integer[]{1, 2, 3};
  } else {
     if (!Intrinsics.areEqual(var4, Reflection.getOrCreateKotlinClass(String.class))) {
        throw (Throwable)(new IllegalArgumentException("Invalid class: " + Reflection.getOrCreateKotlinClass(String.class).getQualifiedName()));
     }

     var10000 = new String[]{"a", "b", "c"};
  }

  String[] test2 = (String[])((Object[])var10000);
}

考虑到该函数在“ when”块中只有2种情况仅被调用过两次,因此这是非常巨大的。而且它甚至没有任何用处-您已经可以看到if个案例的结果。


正确的方法-将每种类型声明为单独的非内联函数:

fun getArray(key: String, defValue: Array<Int>) : Array<Int>{
    return arrayOf(1, 2, 3)
}

fun getArray(key: String, defValue: Array<String>) : Array<String>{
    return arrayOf("a", "b", "c")
}

您必须编写更多代码,但是它没有上面提到的3个问题中的任何一个。

您也可以通过这种方式获得非常干净的字节码(体积小,性能高),这是与以前相同的示例的反编译字节码,但是使用了非内联函数:

public static final void testArray() {
  String var3 = "key";
  Integer[] var4 = new Integer[0];
  getArray(var3, var4);
  var3 = "key2";
  String[] var5 = new String[0];
  getArray(var3, var5);
}