假设我们有一个匿名类生成器:
// test class...
def fixture = new {
val stubA = 42
val stubB = 24
}
然后我想模式匹配和提取字段如下:
val { stubA } = fixture
不幸的是,Scala没有这个功能(TypeScript有)。
当然,使用case class可以做到这一点:
case class Fixture(stubA: Int, stubB: Int)
def fixture = Fixture(42, 24)
val Fixture(stubA, _)= fixture
显然,这太复杂了;我们只需要命名值。
我能以更简单的方式从匿名类进行模式匹配吗?
3条答案
按热度按时间piah890a1#
不,更进一步说,那个语法并不像你想的那样。匿名类的目的是覆盖一些定义良好的接口的行为。例如,我们可以创建一个实现
Runnable
并具有自定义run()
行为的匿名类。但是他们不能真正有意义地向父级添加开始时不存在的字段或方法。fixture
的类型是Object
。这是因为您创建了Object
的匿名子类。您可以在fixture
上调用Object
,如toString()
或equals(...)
。但是你不能写fixture.stubA
,因为Object
上不存在stubA
。因此,您不仅不能对这些自定义的仅匿名字段和方法进行模式匹配,而且甚至不能使用常规语法调用它们。你只能使用Java反射来访问它们。[1]
底线是:如果你正在尝试用Scala编写JavaScript(或Typescript),你将面临一段艰难的时期。这两种语言有着非常不同的解决问题的方法,您刚刚找到了一个很好的例子。类型脚本代码中充斥着满足某些结构化接口的对象文字,但匿名类示例在Scala中并不常见。这就是为什么Scala为我们提供了
case class
es和合成apply
构造函数以及所有其他简化类声明的方便方法。我们应该自由地宣布阶级。[1]即使是用Java反射来做,也充满了地雷。你必须知道Scala如何编译示例变量。Java Reflection看到0参数方法
int stubA()
和int stubB()
。这些字段本身是私有的,所以它们可以被getDeclaredFields()
检测到,但不能被getFields()
检测到。底线是:真是一团糟。am46iovg2#
如果你修改了你的代码,变成这样:
那么你就可以创造出一个精致的类型。实际上,Scala编译器总是在你做
new Type { /* new definitions */ }
之类的事情时创建精炼的类型,但根据版本的不同,它可能会将它向上转换为你精炼的类型,除非你显式地告诉它保留精炼。你能把它和这个做个比对吗?
为了能够进行模式匹配,你必须有一个提取器(一个定义了
unapply
方法的值),它不能只是{}
。例如,你可以这样写:但是,与使用一次性
case class
相比,这可能看起来工作量太大。如果这个“匿名类生成器”也会生成一些别名和一些提取器,那么这个问题就可以解决了(尽管它仍然是非惯用的)。
最后,可以使用一些白盒宏/透明内联来生成这个
unapply
方法实现。但是,虽然它可以工作,但它不会与IDE非常合作。所以,我仍然会使用单一用例类或没有模式匹配/解构的精炼类型(或评论者建议的元组)。因为它与现有的工具相配合。
vnzz0bqm3#
我不知道你对你后面的片段有什么“复杂”的地方。你必须给你的班级命名?在我的书中,复杂的事情很难被称为“复杂性”,我会说,这是相当平凡的。
无论如何,从你的问题中我们并不清楚你到底想达到什么目的,但是你所写的内容让人想起了人们有时在scala中用来创建单元测试的模式:
这个想法是你创建你的
Fixture
trait,在那里你为所有的测试定义你的存根和公共行为。然后,每个测试都发生在该fixture的匿名子类的构造函数体中。这样,在测试中,您就可以访问所有的公共定义,并且可以根据特定测试用例的需要覆盖它们。