kotlin 与java 接口不兼容@FunctionalInterface
需求:获取当前方法名
为了获取当前方法名已知的有1.堆栈获取2.通过classs 的enclosingMethod.name
比较准确的是enclosingMethod,但是为了获取这个还需要一个内部类,于是用到了Runable,但是run方法没有返回值
于是出现了神奇问题
1.照Runnable 抄一份:源码如下:
@FunctionalInterface public interface Runnable { public abstract void run(); }
2.自定义接口 kotlin 版本会自动给转换成为
fun interface IRunnable { fun run(): String? }
使用方法如下:
`class Internal() {
fun bilibli() {
val runnable = IRunnable {
val methodName = object : Any() {}.javaClass.enclosingMethod.name
Log.d("测试", "bilibli当前方法名:${methodName}")
methodName
}
val name = runnable.run()
Log.d("测试", "bilibli当前方法名:${name}")
}
fun alibaba() {
val runnable = Runnable {
val methodName =
object : Any() {}.javaClass.enclosingMethod.name
Log.d("测试", "alibaba当前方法名:$methodName")
return@Runnable
}
runnable.run()
}
`
问题来了:
回合1:结果
alibaba:打印出来了预期值alibaba
bilibli 打印返回竟然是:run
于是不服更改方式
尝试1:更改接口:
@FunctionalInterface interface IRunnable { fun run(): String? }
回合2:依旧是
alibaba:打印出来了预期值alibaba
bilibli 打印返回竟然依旧是:run
无解,查看字节码反编译文件如下:
` public final class Internal {
public final void bilibli() {
IRunnable runnable = com.example.demo.Internal::bilibli$lambda$1;
String name = runnable.run();
Log.d("测试", "bilibli当前方法名:" + name);
}
public final void alibaba() {
Runnable runnable = com.example.demo.Internal::alibaba$lambda$2;
runnable.run();
}
private static final String bilibli$lambda$1() {
String methodName = (new Object() {
}).getClass().getEnclosingMethod().getName();
Log.d("测试", "bilibli当前方法名:" + methodName);
return methodName;
}
private static final void alibaba$lambda$2() {
String methodName = (new Object() {
}).getClass().getEnclosingMethod().getName();
Log.d("测试", "alibaba当前方法名:" + methodName);
}
'
汇合3:无奈,只能将IRunnable改成java
@FunctionalInterface public interface IRunnable { public abstract String run(); }
结果:
alibaba:打印出来了预期值alibaba
bilibli 打印返回竟然依旧是:bilibli
目前看达到预期:
此处有些疑惑不解,java@FunctionalInterface interface 和kotlin fun interface虽然有所同,但是kotlin用@FunctionalInterface interface也依旧无效,不知原因点在哪,字节码也是相同。肾是困惑
隧
德玛西亚勇士:
java @FunctionalInterface interface 与kotlin fun interface区别
GPT:
Java 的 @FunctionalInterface 和 Kotlin 的 fun interface 都用于定义函数式接口,但它们在语法和使用上存在一些差异。以下是它们之间的主要区别:
Java 的 @FunctionalInterface:
注解:
@FunctionalInterface 是一个注解,用于标记接口,并指示该接口符合函数式接口的约定(即有且只有一个抽象方法)。
抽象方法数量:
Java 的函数式接口只能有一个抽象方法。它可以有多个默认方法(使用 default 关键字定义的方法)和静态方法,但这些不算作抽象方法。
实现:
Java 的函数式接口可以用 lambda 表达式、方法引用或传统的方式来实现。
泛型:
Java 的函数式接口可以包含泛型类型参数。
编译时检查:
如果一个接口使用了 @FunctionalInterface 注解但不符合单一抽象方法的约定,编译器会报错。
Kotlin 的 fun interface:
接口声明:
Kotlin 中的 fun interface 是一个特殊的接口声明关键字,用于定义函数式接口,而不是一个注解。
抽象方法数量:
Kotlin 的 fun interface 也只能有一个抽象方法,这一点与 Java 的函数式接口相同。
实现:
Kotlin 的 fun interface 可以用 lambda 表达式、匿名函数或传统的方式来实现。
泛型:
Kotlin 的 fun interface 也支持泛型类型参数。
默认方法和静态方法:
Kotlin 的 fun interface 可以有默认方法和静态方法,但默认方法必须提供实现,因为 Kotlin 不支持 Java 风格的接口默认方法。
编译时检查:
Kotlin 编译器会自动检查 fun interface 是否符合单一抽象方法的约定,不需要额外的注解。
示例对比:
Java 的 @FunctionalInterface 示例:
@FunctionalInterface
public interface Consumer
void accept(T t);
}
Kotlin 的 fun interface 示例:
fun interface Consumer
fun accept(t: T)
}
主要区别:
语法:Java 使用注解来标记函数式接口,而 Kotlin 使用特殊的声明关键字。
默认方法:Java 的函数式接口可以包含有 default 关键字的默认方法,而 Kotlin 的 fun interface 中的默认方法必须提供实现。
编译时检查:Kotlin 的 fun interface 不需要注解来进行编译时检查,因为这是语言的一部分。
总的来说,@FunctionalInterface 和 fun interface 在功能上是相似的,都是用于定义只有一个抽象方法的接口,以便用于 lambda 表达式和方法引用。它们的主要区别在于语法和 Kotlin 对默认方法的支持方式。
德玛西亚勇士
那么kotlin 如果用@FunctionalInterface 注解接口为什么也不行呢
在 Kotlin 中,使用 Java 的 @FunctionalInterface 注解来标记一个接口并不会产生错误,因为 Kotlin 兼容 Java 的注解。然而,这个注解在 Kotlin 中没有特殊的意义,因为 Kotlin 有自己的 fun interface 关键字来定义函数式接口。
以下是为什么在 Kotlin 中使用 @FunctionalInterface 可能不是最佳实践的原因:
语义差异:
Kotlin 的 fun interface 是语言的一部分,它有明确的语义,即定义一个函数式接口。而 @FunctionalInterface 是 Java 的注解,Kotlin 编译器不会对其做特殊处理。
编译时检查:
Kotlin 编译器会自动检查 fun interface 是否符合单一抽象方法的约定。使用 @FunctionalInterface 不会提供额外的编译时检查,因为 Kotlin 编译器不会识别这个注解的语义。
代码风格:
在 Kotlin 中使用 fun interface 更符合 Kotlin 的编码风格和习惯。使用 Java 的注解可能会让 Kotlin 代码看起来不地道,也不符合 Kotlin 的最佳实践。
可读性和维护性:
fun interface 在 Kotlin 代码中更易于理解,因为它清晰地表明了接口的意图。而 @FunctionalInterface 可能会让不熟悉 Java 的开发者感到困惑。
如果你在 Kotlin 中使用 @FunctionalInterface 注解,它不会阻止你的代码编译或运行,但它也不会提供任何在 Kotlin 中定义函数式接口的好处。因此,如果你想要在 Kotlin 中定义一个函数式接口,你应该使用 fun interface 关键字,而不是 @FunctionalInterface 注解。
德玛西亚勇士
在 Kotlin 中,虽然可以使用 Java 的 @FunctionalInterface 注解来标记一个接口,但它不会影响 Kotlin 编译器对接口的处理。下面我会进一步解释为什么在 Kotlin 中使用 @FunctionalInterface 并不是必须的,以及如何正确地在 Kotlin 中定义函数式接口。
Kotlin 中的函数式接口
在 Kotlin 中,函数式接口是通过 fun interface 关键字来定义的。这个关键字告诉编译器这个接口是函数式接口,并且应该有一个唯一的抽象方法。下面是使用 fun interface 定义函数式接口的例子:
fun interface Consumer
fun accept(t: T)
}
在这个例子中,Consumer 是一个函数式接口,它有一个抽象方法 accept。这个接口可以用于 lambda 表达式或者方法引用。
为什么 @FunctionalInterface 在 Kotlin 中不是必须的
Kotlin 的 fun interface 更简单:
Kotlin 的 fun interface 关键字直接集成到语言中,使得定义函数式接口更加简单和直观。
编译器检查:
Kotlin 编译器会自动检查 fun interface 是否只有一个抽象方法,而不需要额外的注解。
类型推导:
Kotlin 的类型推导和 lambda 表达式的语法糖使得使用函数式接口非常方便,无需额外的注解。
使用 @FunctionalInterface 的问题
如果你在 Kotlin 中错误地使用 @FunctionalInterface 注解,可能会遇到以下问题:
误解:其他开发者可能会误解 @FunctionalInterface 的用途,因为它在 Kotlin 中没有特殊的意义。
冗余:在 Kotlin 中,@FunctionalInterface 注解是多余的,因为 fun interface 已经提供了所需的功能。
潜在的错误:如果你错误地使用了 @FunctionalInterface 注解,Kotlin 编译器不会阻止你,这可能会导致潜在的逻辑错误或者不符合函数式接口的预期行为。
正确的做法
在 Kotlin 中,如果你想定义一个函数式接口,你应该使用 fun interface 关键字。如果你需要与 Java 代码交互,并且 Java 代码期望一个 @FunctionalInterface,你可以选择在 Kotlin 接口中添加 @FunctionalInterface 注解,但这不是必须的,也不建议这样做,除非你确实需要兼容 Java 的某些特定场景。
下面是一个正确的 Kotlin 函数式接口的例子:
fun interface Consumer
fun accept(t: T)
}
// 使用 lambda 表达式创建 Consumer 实例
val consumer: Consumer
在这个例子中,我们定义了一个 Consumer 函数式接口,并用一个 lambda 表达式创建了一个实例。这是 Kotlin 中的标准做法。