Kotlin классы, не идентифицированные как Flink действительные POJO - PullRequest
0 голосов
/ 18 января 2019

Я пишу приложение Flink на Kotlin, и классы данных (как и другие классы Kotlin) не определены как допустимые типы POJO.

Документация Flink гласит, что тип данных распознается как тип POJO (и допускает ссылку на поле «по имени»), если выполняются следующие условия:

  • Класс публичный и автономный
  • У класса есть открытый конструктор без аргументов
  • Все нестатические, непереходные поля в классе являются либо общедоступными (и не окончательными), либо имеют общедоступные методы получения и установки, которые следуют соглашениям об именах Java-бинов.

Я получаю следующее при реализации класса данных 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.

Продолжая исследование, я рассмотрел метод 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 распознает поля как окончательные.

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

Однако для этих полей создаются общедоступные методы получения и установки, поэтому этот объект все равно должен соответствовать критериям 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)]

Методы получения и установки, однако, являются окончательными, что заставляет меня поверить, что это проблема.

Я относительно новичок в разработке JVM, поэтому любая помощь будет принята с благодарностью. Я просмотрел список рассылки Flink Jira, Stack Overflow и Flink и не обнаружил аналогичных проблем.

1 Ответ

0 голосов
/ 19 января 2019

Я вижу как минимум два нарушения правил 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-бинов.

Поскольку вы используете ключевые слова val, сгенерированные поля будут final, и для них не будет сгенерировано никакого установщика. Таким образом, вы можете изменить val с на var с, и поля больше не будут final, а также будут генерироваться правильные методы получения и установки. (Или вы можете использовать другую аннотацию, чтобы предотвратить генерацию геттеров и сеттеров и выставить поле как https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html#instance-fields)

итоговый код должен быть таким:

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