【问题标题】:Kotlin classes not identified as Flink valid POJO'sKotlin 类未识别为 Flink 有效 POJO
【发布时间】:2019-06-13 02:20:53
【问题描述】:

我正在用 Kotlin 编写 Flink 应用程序,并且数据类(以及其他 Kotlin 类)未被识别为有效的 POJO 类型。

Flink 文档指出,如果满足以下条件,则数据类型被识别为 POJO 类型(并允许“按名称”字段引用):

  • 该类是公共的且独立的
  • 该类有一个公共的无参数构造函数
  • 类中的所有非静态、非瞬态字段要么是公共的(非最终的),要么具有遵循 Java bean 命名约定的公共 getter 和 setter 方法。

我在实现 Kotlin 数据类时收到以下信息,它应该满足上述条件才能被识别为 POJO:

[main] INFO org.apache.flink.api.java.typeutils.TypeExtractor - 
Class class <Class> cannot be used as a POJO type because not all 
fields are valid POJO fields, and must be processed as GenericType. 
Please read the Flink documentation on "Data Types & Serialization" 
for details of the effect on performance.

进一步调查,我查看了 Flink 的 TypeExtractor.isValidPojoField 方法@https://github.com/apache/flink/blob/master/flink-core/src/main/java/org/apache/flink/api/java/typeutils/TypeExtractor.java

在一个单独的项目中,我使用 java.lang.reflect.Modifier 对一个简单的 Kotlin 数据类应用了字段检查,以试图缩小问题范围。

data class SomeDataClass(
    val topic: String = "",
    val message: String = ""
)

虽然 Kotlin 类字段默认具有公共可见性,但 Modifier.isPublic 将这些字段识别为私有。此外,Modifier.isFinal 将字段识别为 final。

val clazz = SomeDataClass::class.java
val fields = clazz.declaredFields
fields.forEach { it ->
    println("field: $it")
    println(it.genericType)
    println("public? " + Modifier.isPublic(it.modifiers))
    println("final? " + Modifier.isFinal(it.modifiers))
    println("transient? " + Modifier.isTransient(it.modifiers))
    println("static? " + Modifier.isStatic(it.modifiers))
}

>
field: private final java.lang.String SomeDataClass.topic
class java.lang.String
public? false
final? true
transient? false
static? false

但是,为这些字段创建了公共 getter 和 setter 方法,因此该对象仍应符合 POJO 标准。

println(clazz.declaredMethods.toList())

>
[public boolean SomeDataClass.equals(java.lang.Object), 
public java.lang.String SomeDataClass.toString(), 
public int SomeDataClass.hashCode(), 
**public final java.lang.String SomeDataClass.getMessage(),** 
public final SomeDataClass SomeDataClass.copy(java.lang.String,java.lang.String), 
**public final java.lang.String SomeDataClass.getTopic(),** 
public final java.lang.String SomeDataClass.component1(), 
public final java.lang.String SomeDataClass.component2(), 
public static SomeDataClass SomeDataClass.copy$default(SomeDataClass,java.lang.String,java.lang.String,int,java.lang.Object)]

然而,getter 和 setter 方法是最终的,这让我相信这是问题所在。

我对 JVM 开发比较陌生,因此我们将不胜感激。我查看了 Flink Jira、Stack Overflow 和 Flink 邮件列表,没有发现类似的问题报告。

【问题讨论】:

  • 如果问题只是数据类的最终修饰符,您可以使用“全开放”kotlin 插件将它们从编译代码中删除。

标签: kotlin apache-flink


【解决方案1】:

我发现提供的数据类至少有两个 POJO 规则违规。

1) 该类有一个公共的无参数构造函数

默认情况下,Kotlin 不会为具有默认参数值的函数生成重载 (https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html#overloads-generation)

所以你编译的类将只有一个带有两参数构造函数的构造函数,并且不会创建无参数构造函数。要强制 Kotlin 编译器生成多个重载,应该使用 @JvmOverloads 注释。在您的情况下,它将用于构造函数,因此我们还需要添加 constructor 关键字:

data class SomeDataClass @JvmOverloads constructor

2) 类中的所有非静态、非瞬态字段要么是公共的(非最终的),要么具有遵循 Java bean 命名约定的公共 getter 和 setter 方法。

由于您使用val 关键字,生成的字段将为final,并且不会为它们生成setter。因此,您可以将vals 更改为vars,字段将不再是final,并且也会生成正确的getter 和setter。 (或者您可以使用另一个注释来防止生成 getter 和 setter 并公开字段,因为它是 https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html#instance-fields

所以最终的代码应该是这样的:

data class SomeDataClass @JvmOverloads constructor(
    var topic: String = "",
    var message: String = ""
)

【讨论】:

  • 第一个问题不适用,从您链接的文档中:“如果一个类具有所有构造函数参数的默认值,则会为其生成一个公共无参数构造函数。即使@JvmOverloads 未指定注解。”
猜你喜欢
  • 2017-10-26
  • 2018-08-06
  • 2021-06-20
  • 2015-04-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-12
  • 1970-01-01
相关资源
最近更新 更多