Scala 自定义集合返回 null 列表作为默认值

Posted

技术标签:

【中文标题】Scala 自定义集合返回 null 列表作为默认值【英文标题】:Scala custom collection returns list of null as default values 【发布时间】:2021-07-24 18:05:13 【问题描述】:

我创建了一个名为Matrix 的自定义集合,它以一维List 的形式存储一个矩阵(这是一种要求,必须这样)。问题是,我想实现这个适用于泛型的集合。但是当我执行它并打印返回的集合时,它会给出null 值。

我创建了一个外部类来帮助为每种类型自定义默认值。

现在,sn-ps:

输出

> sbt run
[info] welcome to sbt 1.4.9 (N/A Java 15.0.2)
[info] loading project definition from /home/pace/Documents/git/1024-scala/1024-scala/project
[info] loading settings for project root from build.sbt ...
[info] set current project to scalatest-example (in build file:/home/pace/Documents/git/1024-scala/1024-scala/)
[info] compiling 1 Scala source to /home/pace/Documents/git/1024-scala/1024-scala/target/scala-2.13/classes ...
[info] done compiling
[info] running matrix.MainClass 
Matrix(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null)
[success] Total time: 4 s, completed May 1, 2021, 9:10:28 PM

矩阵集合实现:

package matrix

import scala.collection.Iterable
import scala.collection.Iterator
import scala.collection.AbstractIterator

import defaultValues.Default

class Matrix[A] private (val col: Int, val row: Int, elems: List[A])
    extends Iterable[A]  self =>

  def this() = this(col = 4, row = 4, elems = List.fill(4 * 4)(Default.value[A]))

  def this(s: Int) = this(col = s, row = s, elems = List.fill(s * s)(Default.value[A]))

  def this(c: Int, r: Int) = this(col = c, row = r, elems = List.fill(c * r)(Default.value[A]))

  def apply(i: Int): A = elems(i).asInstanceOf[A]

  def iterator: Iterator[A] = new AbstractIterator[A] 
    private var current = 0
    def hasNext = current < elems.size
    def next(): A = 
      val elem = self(current)
      current += 1
      elem
    
  

  override def className = "Matrix"


object MainClass extends App 
  val m = new Matrix[Int]()
  println(m)

以及我为每种类型获取 默认值 的方式(从其他 SO 线程获取,我已经检查过它在 Scala REPL 中是否有效):

package defaultValues

import scala.collection.immutable

class Default[+A](val default: A)

trait LowerPriorityImplicits 
  // Stop AnyRefs from ***ing with AnyVals
  implicit def defaultNull[A <: AnyRef]: Default[A] = new Default[A](null.asInstanceOf[A])


object Default extends LowerPriorityImplicits 
  implicit object DefaultString extends Default[String]("")
  implicit object DefaultDouble extends Default[Double](0.0)
  implicit object DefaultFloat extends Default[Float](0.0F)
  implicit object DefaultInt extends Default[Int](0)
  implicit object DefaultLong extends Default[Long](0L)
  implicit object DefaultShort extends Default[Short](0)
  implicit object DefaultByte extends Default[Byte](0)
  implicit object DefaultBoolean extends Default[Boolean](false)
  implicit object DefaultUnit extends Default[Unit](())

  implicit def defaultSeq[A]: Default[immutable.Seq[A]] = new Default[immutable.Seq[A]](immutable.Seq())
  implicit def defaultSet[A]: Default[Set[A]] = new Default[Set[A]](Set())
  implicit def defaultMap[A, B]: Default[Map[A, B]] = new Default[Map[A, B]](Map[A, B]())
  implicit def defaultOption[A]: Default[Option[A]] = new Default[Option[A]](None)

  def value[A](implicit value: Default[A]): A = value.default

我正在运行Scala 2.13

你能帮我一把吗?谢谢。

【问题讨论】:

【参考方案1】:
  def this() = this(col = 4, row = 4, elems = List.fill(4 * 4)(Default.value[A]))

因为A 没有约束,所以你告诉编译器这个函数应该适用于每一个A 可能,这意味着A 必须是这里的最大类型斯卡拉-AnyRef。现在 A 被解析为 AnyRefDefault.value[A] 始终解析为 defaultNull,无论您在呼叫站点放置什么。这是有道理的,因为您说它应该适用于所有可能的类型。

解决方案只是在 A 中放置一个约束,以便编译器不会将其推断为最大类型:

class Matrix[A: Default] 
  def this() = this(col = 4, row = 4, elems = List.fill(4 * 4)(Default.value[A]))


// equivalent to this form
def apply[A: Default]: Matrix[A] = new Matrix(col = 4, row = 4, elems = List.fill(4 * 4)(Default.value[A]))

// which is syntactic sugar for
def apply[A](implicit default: Default[A]): Matrix[A] = new Matrix(col = 4, row = 4, elems = ???

通过将约束放在那里,您将解决 A 的问题推迟到实际使用情况(调用 new Matrix[Int]),因为现在编译器需要知道您拥有的实际 A 以便它可以搜索隐式参数。

【讨论】:

作为 Scala 的初学者。介意你告诉我它是如何在内部工作的吗?它链接 A到Default中的implicit objects?或者它具体做什么?我不介意它是否是一个更数学的解释,只要它是可以理解的。 [A: Default][A](implicit default: Default[A]) 的语法糖。在英语中,它的意思是:调用这个函数需要一些类型A,和一个类型为Default[A]的隐式参数,绑定到名称default。这就是 Scala 中类型类模式的编码方式。这里有几个机制:隐式参数,泛型类型参数的使用,......我建议你看看books.underscore.io/scala-with-cats/scala-with-cats.html并阅读第1章来了解类型类。如有任何进一步的问题,请随时跳到 gitter 频道 scala/scala 中

以上是关于Scala 自定义集合返回 null 列表作为默认值的主要内容,如果未能解决你的问题,请参考以下文章

scala中 sorted,sortBy,sortWith语法使用

scala中 sorted,sortBy,sortWith语法使用

Android 自定义 ListView onClickListener 返回 null

scala的多种集合的使用之遍历集合的方法

scala的基础概念

使用自定义逻辑过滤对象的 scala 集合