【问题标题】:Scala objects constructed inside if在 if 内部构造的 Scala 对象
【发布时间】:2021-04-30 22:53:55
【问题描述】:

我想做一些我觉得很简单的事情,但我不知道怎么做。它如下:根据一些字符串变量,我想创建一个特定类型的对象。但是,底层对象具有相同的方法,因此我希望能够在创建对象的 if 块之外使用该对象。实现它的最佳方法是什么?

为了说明我的需要,这里是代码:

var model = null
if (modelName == "KMeans") {
  model = new KMeans()
} else if (modelName == "BisectKMeans") {
  model = new BisectingKMeans()
}
model.setK(k)
model.fit(data)

KMeansBisectingKMeans 都有 setKfit 方法。我想在 if 块内创建对象,但在其外部使用对象。此代码在声明变量时出错,因为我没有对其进行初始化。

我已尝试使用案例类并将变量声明为 Any 类型的通用对象,但无法使其工作,实现我正在寻找的最佳方法是什么?

【问题讨论】:

  • 这些对象是你控制的还是由第三方库提供的?
  • 它们由库 Spark MLLib 提供。更准确地说,它们是这些类:KMeansBisectingKMeans

标签: scala instantiation


【解决方案1】:

Scala 实际上确实允许您使用structural types 执行此操作:

type Model = { 
  def setK(k: Int): Any
  // the links in the question don't have a fit method
  def fit(???): ???
}

val model: Model = if (modelName == "KMeans") { new KMeans() } else { model = new BisectingKMeans() }
model.setK(k)
model.fit(data)

但如果由于使用反射而有更好的选择,则不特别推荐使用。在这种特定情况下,我会在块内简单地调用setKfit;如果没有,请创建自己的 KMeansBisectingKMeans 包装器,它们实现了一个共同的特征。

【讨论】:

    【解决方案2】:

    创建您自己的接口并根据该接口调整实现。如果您需要将非常不同的类(例如不同的方法名称)统一到一个通用接口中,这也将起作用。不需要使用implicit def,但会在调用站点保存包装部分

    trait Model {
        def setK(): ??? // fill in your type
        def fit(): ??? // fill in your type
    }
    
    object Model {
        implicit def kmeansModel(kmean: Kmeans): Model = new Model {
            def setK() = kmeans.setK() // delegate to actual Kmeans
            def fit() = kmeans.fit() // delegate to actual Kmeans
        }
        
        implicit def bisectingKmeansModel(): Model = ??? // similarly
    
    // usage
    val model: Model = if (modelName == "KMeans") {
      new KMeans()
    } else if (modelName == "BisectKMeans") {
      new BisectingKMeans()
    } else {
      ??? // other cases
    }
    
    model.setK()
    model.fit()
    

    【讨论】:

    • 您可能应该拥有类似于kmeansModelimplicit def bisectingKmeansModel(kmean: BisectingKMeans): Model
    • @AlexeyRomanov 感谢您的关注!我在第一次迭代时就达到了 typeclass :)
    【解决方案3】:

    为了调用方法.setK().fit(),编译器必须“知道”变量model 是具有这些方法的特定类型。你想说的是,“变量可能是这种类型,或者它可能是那种类型,但它们都有这些方法,所以没关系。”

    编译器不这么看。它说,“如果它可能是 A,也可能是 B,那么它必须是 LUB(最小上限),即它们都继承自的最近类型。”

    这是实现目标的一种方法。

    class KMeans {  //parent type
      def setK(): Unit = ???
      def fit(): Unit = ???
    }
    class BisectingKMeans extends KMeans {
      override def setK(): Unit = ???
      override def fit(): Unit = ???
    }
    
    val model =
      if (modelName == "KMeans")
        new KMeans()
      else //always end with "else", never "else if"
        new BisectingKMeans()
    
    model.setK()
    model.fit()
    

    【讨论】:

    • 感谢您的回复。尝试您的回复后我遇到的问题是 LUB 没有 setK 和 fit 方法,因此它给了我一个编译错误。我也无法更改课程,因为它们来自图书馆(请参阅我原来问题中的评论)。
    • 如果你不能改变类,那么你就不能尝试我建议的解决方案,所以它当然不起作用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-14
    • 2013-06-17
    • 1970-01-01
    • 1970-01-01
    • 2016-06-19
    相关资源
    最近更新 更多