【问题标题】:Kotlin Generics - recursive generic - casting the instance of erased type failsKotlin Generics - 递归泛型 - 转换擦除类型的实例失败
【发布时间】:2020-11-19 11:55:29
【问题描述】:

当我遍历 Sprite 列表并检查它们是否实现 UsesObjectPool 时,由于类型擦除,我遇到了这些问题:

*如果我尝试检查它的类型,我得到:

无法检查已擦除类型的实例:UsesObjectPool

  if(it is UsesObjectPool<Sprite>)
        {
            it.objectPool.releaseObject(it)
        }



  • 如果我使用通配符 * 来检查,它会说:

"必填参数Nothing,找到Sprite & UsesObjectPool:

  if(it is UsesObjectPool<*>)
            {
                it.objectPool.releaseObject(it)
            }



*如果我将它转换为 UsesObjectPool,我会得到:

类型参数不在其范围内。预期的: 找到 UsesObjectPool:Sprite

 if(it is UsesObjectPool<*>)
            {
                (it as UsesObjectPool<Sprite>).objectPool.releaseObject(it)
            }



泛型类和接口:

interface UsesObjectPool<T> where T :UsesObjectPool<T>, T : Sprite
     {
         val objectPool: BaseObjectPool<T>
     }
    
abstract class BaseObjectPool<T> where T : UsesObjectPool<T>, T : Sprite
    {
        fun releaseObject(instance: T)
       {
          //Implementation
       }
    }

【问题讨论】:

    标签: java android kotlin generics


    【解决方案1】:

    这里的问题是您实际上并没有说T 应该是实现类型(在某些语言中称为Self)。就 Kotlin 编译器而言,T 可以是实现UsesObjectPool&lt;T&gt; 的任何其他类型。这就是为什么你不能将it 传递给releaseObject

    这是一个例子:

    class EvilObjectPool<T>: BaseObjectPool<T>() where T : UsesObjectPool<T>, T : Sprite
    
    class Implementation : Sprite(), UsesObjectPool<Implementation> {
        override val objectPool: BaseObjectPool<Implementation>
            get() = EvilObjectPool()
    
    }
    class EvilImplementation : Sprite(), UsesObjectPool<Implementation> {
        override val objectPool: BaseObjectPool<Implementation>
            get() = EvilObjectPool()
    
    }
    

    注意这个编译。一般来说,我们人类认识到这种“自绑定泛型”模式,并且永远不会写像EvilImplementation 这样的东西,但编译器不知道:(

    现在想象itEvilImplementationit.objectPool.releaseObject 会接受Implementation,但你给它的是EvilImplementation

    无论如何,我认为您需要重新考虑您的设计,因为 Kotlin 不像其他一些语言那样支持 Self

    另见a similar problem in Java

    【讨论】:

    • 既有趣又可笑。现在我不知道我应该如何处理新设计。如果您有任何想法或提示并将其添加到您的答案中,我们将不胜感激。
    • @DeveloperKurt 我想说这里的信息太少了。例如,每种类型的用途是什么,它们代表什么?为什么releaseObject 应该接受T?我建议提出一个包含更多上下文的新问题,特别是询问如何重新设计您的模型。
    • UsesObjectPool 接口用于区分由对象池而不是垃圾收集器创建和释放的 Sprite 类型。为了能够释放它们,精灵类型的对象池对其精灵字段和对象池的字段进行操作。
    • 我的个人意见是,抛弃UsesObjectPool,在Sprite 中添加一个releaseIfNeeded 方法。我开始认为这可能更适合 SoftwareEngineering.SE 或 Programmers.SE 而不是 SO...@DeveloperKurt
    • 我在考虑您的答案,但我认为对 T 是什么存在误解。 T 是 SPRITE_TYPE。它不会扩展 BaseObjectPool,并且其他 ObjectPool 不会有自己的泛型类型,它们只会将 Sprite 类型传递给 BaseObjectPool。
    猜你喜欢
    • 1970-01-01
    • 2018-05-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多