我有以下情况:
得到一个方法def f(lst: List[Any])
,它对列表进行一些转换并返回该转换的结果(所有Any
都是case类)。我需要完成的是当输入列表为空时,生成一个列表,其中包含一个正确类型的元素并使用它进行转换。
是否可以在类型级别上保证某些case类具有no-arg构造函数?如果是这样,应该用Any
替换什么?如果不是,最好的方法是什么?也许我应该将我的方法改为def f[T](lst: List[T], default: T)
?
任何帮助表示感谢。
答案 0 :(得分:5)
你在找这样的东西吗?
import scalaz._
import Scalaz._
scala> def f[A : Zero](lst: List[A]) = {
| val xs = if(lst.isEmpty) List(mzero[A]) else lst
| xs ++ xs // some transformation
| }
f: [A](lst: List[A])(implicit evidence$1: scalaz.Zero[A])List[A]
scala> f(List.empty[Int])
res1: List[Int] = List(0, 0)
scala> f(List("hello", "world"))
res2: List[java.lang.String] = List(hello, world, hello, world)
如果是,你可以参考我前段时间写过的this post。
答案 1 :(得分:2)
简单的答案是否定的,类型系统无法告诉您类是否具有默认构造函数。请记住,case类通常没有默认构造函数,因为不推荐使用no-arg case类。对于不可变对象,默认构造函数的概念并不那么有用。 AFAIK没有理由不应该原则上(Scala支持结构类型,其中类型必须具有特定名称的方法),但它需要语言更改。您可以在运行时使用反射检查,但这不是您想要的。
但是,您可以使用类型类模式强制将默认值放在范围内。这在概念上非常类似于在OP中添加额外的默认争论,但使用隐含来隐藏它们。它在集合库中大量使用。 missingfaktor使用scalaz.Zero
的答案是一个特例,但在vanilla Scala和一些不一定是零的任意默认值下都很容易做到。
case class Default[T](default: T)
case class Foo(value: String)
case class Bar(value: Int)
implicit val fooDefault = Default(Foo("I'm a default Foo")) // note 1
现在让我们看一个示例用法:
def firstItem[T](lst: List[T]) (implicit ev: Default[T]) = // note 2
if (lst.isEmpty) ev.default else lst.head
val fooList = List(Foo("cogito"), Foo("ergo"), Foo("sum"))
val emptyFooList = List[Foo]()
val barList = List(Bar(101), Bar(102))
val emptyBarList = List[Bar]()
firstItem(fooList) // Foo("cogito")
firstItem(emptyFooList) // Foo("I'm a default Foo")
firstItem(barList) // ** error: missing implicit **
因此,我们发现这会使用List[Foo]
进行编译,但不接受List[Bar]
,因为没有隐式Default[Bar]
(注3)。
注1:这个隐式可以在object Foo
上定义 - 如果你将类导入其他地方,这将确保它在范围内。但它不一定是:你也可以为任意类定义类似的含义,Int
,String
,无论如何(尝试它)。
注意2:这等于加糖版本def firstItem[T: Default](lst: List[T]) =
...,您将ev
召唤到implicitly[Default[T]]
。随便挑选。
注意3:我们可以通过简单地提供一个来实现它:
firstItem(barList)(Default(Bar(42))) // Bar(101)
firstItem(emptyBarList)(Default(Bar(42))) // Bar(42)
答案 2 :(得分:1)
我不确定你要做什么(也许你可以包含更多细节),但我可以立即给出的一些建议是,如果你有一堆相关的案例类,它们都应该扩展一个密封特性。这不仅可以让您拥有更好的类型安全性(不再是Any
),而且编译器将能够检查详尽的模式匹配。例如:
sealed trait Foo
case class Bar(x: Int) extends Foo
case class Baz(y: String) extends Foo
然后你可以像这样定义你的功能
def f[T <: Foo](lst: List[Foo], default: T)//...
这将允许list
包含任何案例类的项目,但要求default
是type参数指定的类型(必须是{{1}的子类型})