首页 > 解决方案 > Ununderstood type variance using kotlin variance

问题描述

I'm using generics to not reuse code and I' running into a lack of understanding for type generics. I have a class Writer (java code from another library).

public class Writer<T>

A class FileWriter (java code from another library)

public class FileWriter<D>{

  FileWriter(Writer<D> writer){
    this.writer=writer
  }
  
  public void append(D datum){
  //Does something
  }
}

Now I'm initiating this in kotlin:

val writer = FileWriter(Writer(AGenratedJavaClassThatIMplementsSpecificRecord::class.java))

I can now call writer.append with AGenratedJavaClassThatIMplementsSpecificRecord(). It works just fine

I would like to pass this writer to a function.

funDoSomethingExtra(writer: FileWriter<in SpecificRecord>)

This gives me an error that I do not understand.

Type mismatch: inferred type is FileWriter<AGenratedJavaClassThatIMplementsSpecificRecord!>! but FileWriter<in SpecificRecord> was expected

Changing this to

funDoSomethingExtra(writer: FileWriter<out SpecificRecord>)

Makes writers.append give the error

Required Nothing, found AGenratedJavaClassThatIMplementsSpecificRecord.

Without the use of methods, all works fine. Which details Am I missing? It is probably something small,

Kind regards, Jelmew

标签: kotlingenerics

解决方案


这行代码:

val writer = FileWriter(Writer(AGenratedJavaClassThatIMplementsSpecificRecord::class.java))

没有指定 FileWriter 的类型,所以它是从你的参数推断给构造函数的,所以类型writerFileWriter<AGenratedJavaClassThatIMplementsSpecificRecord>.

您的签名funDoSomethingExtra(writer: FileWriter<in SpecificRecord>)对于调用writer对 aSpecificRecord或 的子类型执行某些操作的方法是正确的SpecificRecord。但是,您的

FileWriter<AGenratedJavaClassThatIMplementsSpecificRecord>

不能转换为 aFileWriter<in SpecificRecord>因为AGenratedJavaClassThatIMplementsSpecificRecord是 的子类型SpecificRecord,而不是超类型。编译器知道您的文件编写器可以使用AGenratedJavaClassThatIMplementsSpecificRecord,但它不知道它可以使用不太具体的类型SpecificRecord。您有可能调用超类型中不存在的子类型的某些函数。

所以为了能够将你的 writer 传递给这个函数,它需要是 a FileWriter<SpecificRecord>or FileWriter<in SpecificRecord>。在已经分配了它的类型之后,您不能安全地转换它,但是您可以在声明站点为其分配正确的类型,而不是让编译器尝试推断它:

val writer: FileWriter<SpecificRecord> = FileWriter(Writer(AGenratedJavaClassThatIMplementsSpecificRecord::class.java))

推荐阅读