【问题标题】:Scala: How to insert SubClass instead of Superclass in Map declared for SuperClassScala:如何在为 SuperClass 声明的 Map 中插入 SubClass 而不是 Superclass
【发布时间】:2016-01-19 12:15:09
【问题描述】:

我需要在已为 SuperClass 声明的 HashMap 中使用子类。 例如:我创建了一个超类和一个子类。 现在我创建一个抽象类 A 和一个扩展 A 的具体类 B。 A 有一个变量 myMap ,声明为 java.util.Map[_ <: Superclass, String] 。在 B 类中,我需要使用 myMap ,但我需要在 myMap 中插入 SubClass 的实例而不是 Superclass。以下是解释的代码

abstract class Superclass {
    def foo(value:String): Boolean
}



class Subclass extends Superclass {
    def foo(value:String) = {
        true // or random boolean expression
    }
}

abstract class A {
    var myMap: java.util.Map[_ <: Superclass, String]
    def addToMap(value:String)
}

class B extends A {
    myMap = new java.util.HashMap[Subclass, String]()

    def addToMap(value:String) { 
        val subclass = new SubClass()
        myMap.put(subclass, value) // getting compilation error here
    }
}

如果我尝试在 myMap 中插入子类实例,我会收到以下错误: 类型不匹配;找到:需要 Subclass.type(具有底层类型 Subclass):_$2 其中 type _$2 <: superclass>

【问题讨论】:

    标签: scala class


    【解决方案1】:

    如果你可以在这里使用SubClass,你将违反里氏替换原则:B 类应该能够做所有可以用A 做的事情,但是在你的例子中,你不能填充 @ 987654324@ 键在其Map 中。

    有几种方法可以解决它,例如你可以参数化 AB:

    abstract class A[S <: Superclass] {
      var myMap: java.util.Map[S, String]
    
      def addToMap(value: String)
    }
    
    class B extends A[Subclass] {
    
      var myMap:java.util.Map[Subclass, String] = new java.util.HashMap[Subclass, String]()
    
      def myMap(map: java.util.Map[Subclass, String]) { myMap = map }
    
      def addToMap(value: String) {
        val subclass = new Subclass()
        myMap.put(subclass, value)
      }
    }
    

    类似的解决方案可以使用抽象类型成员:

    abstract class A {
    
      type S <: Superclass
    
      var myMap: java.util.Map[S, String]
    
      def addToMap(value: String)
    }
    
    class B extends A {
    
      type S = Subclass
    
      var myMap: java.util.Map[Subclass, String] = new java.util.HashMap[Subclass, String]()
    
      def myMap(map: java.util.Map[Subclass, String]) {
        myMap = map
      }
    
      def addToMap(value: String) {
        val subclass = new Subclass()
        myMap.put(subclass, value)
      }
    } 
    

    我不确定,为什么需要额外的 setter。

    一般来说,我认为将var 放在抽象类中并不是一个好主意。实现细节应该像这样泄漏吗?可能不是。即使需要,您真的必须能够替换整个地图吗?如果没有,您可以使用val。或者,您可以使用defs 构建一个用于添加、删除、查询元素的外观,而在A 中完全不提及地图。

    【讨论】:

    • 谢谢,但我仍然收到错误消息。现在换了一个。在 B 类定义位置它说:B 类需要是抽象的,因为 java.util.Map[Subclass,String] 类型的 A 类中的变量 myMap 未定义(请注意,变量需要初始化才能定义)跨度>
    • 对此有何建议?
    • 已修复。我不确定为什么B 中需要var,但它现在似乎可以工作了。
    • 在抽象类中,var 可以定义为 def。然后可以在子类中将相同的覆盖声明声明为 val
    猜你喜欢
    • 1970-01-01
    • 2012-11-16
    • 1970-01-01
    • 1970-01-01
    • 2017-02-06
    • 2014-05-24
    • 2014-08-14
    • 1970-01-01
    • 2021-12-12
    相关资源
    最近更新 更多