Kotlin仿制药继承头痛

时间:2016-11-01 18:37:42

标签: android generics kotlin

我是Kotlin的新手,我正在尝试编译此代码但没有成功(这只是我想在一个真实项目中做的一个例子):

abstract class Builder<T: Any, Y: Any> 

class BuilderImpl() : Builder<String, Int>()

abstract class Shape<T: Any>(){
     abstract var builder: Builder<T, *>
}

class Circle() : Shape<String>(){
     override var builder: Builder<String, Int> = BuilderImpl()
}

我想从Circle类中的Shape类覆盖builder属性。 Shape类只知道Builder类中的第一个泛型类型。第二个可以是任何类型,所以我使用*为此。

当我尝试在Circle类中覆盖builder属性时,编译器会抱怨消息:

  "Var-property type is 'Builder<String, Int>', which is not a type of overriden public abstract var builder: Builder<String, *>".

我也尝试使用“out Any”而不是*,但没有成功。

我对如何编译此代码一无所知。

1 个答案:

答案 0 :(得分:3)

当您覆盖var属性时,编译器会强制执行

  • 获取重写属性的值是类型安全的:如果原始属性的类型为S,则重写的属性应返回S的实例,即其类型应为S或其子类型(例如,当需要Int时,可以安全地返回Number;

  • 设置被覆盖属性的值是类型安全的:如果原始属性的类型为S,则被覆盖的属性应接受S的对象作为其值,因此其类型应为是S或其某些超类型(例如,如果我们需要存储String,将其存储为Any就可以了)

鉴于这两个要求,被覆盖的var属性可以拥有的唯一类型是原始属性类型本身,因为其子类型和超类型将违反上述条件之一。例如,重写的属性应该仍然能够存储Builder<T, *>,并且将其类型指定为Builder<T, Int>不是一个选项:

val s: Shape<SomeType> = Circle<SomeType>()
val b: Builder<SomeType, *> = someBuilder

s.builder = b // should be type-safe, but won't be with the override you want to do

因此,当您覆盖它时,您根本无法更改var属性的类型,因为就Kotlin泛型而言,它位于inout位置。

但是,您可以尝试以下选项之一:

  • 更改为val媒体资源。在这种情况下,没有第二个要求,您可以将属性的类型更改为原始属性类型的子类型:

    abstract class Shape<T: Any>(){
        abstract val builder: Builder<T, *>
    }
    
    class Circle() : Shape<String>(){
        override var builder: Builder<String, Int> = BuilderImpl() // OK
    }
    
  • 添加通用参数并在子类中指定它。在这种情况下,您可以使用var,因为Circle将是专业化Shape<String, Int>的子类型,而不是原始类型Shape<T, R>

    abstract class Shape<T: Any, R: Any>(){
        abstract var builder: Builder<T, R>
    }
    
    class Circle() : Shape<String, Int>(){
        override var builder: Builder<String, Int> = BuilderImpl() // OK
    }