让我们从一个故事开始
我与一个名叫 Foo 的虚拟俱乐部有联系,人们已经注册参加派对。经理要求我维护成员名称和成员ID 的单独列表。有一天,经理来找我,告诉我这个问题。我作为开发人员玩scala给了他像前面提到的解决方案。
的类
case class NonEmptyFoo(bar1:String,bar2:String)
在俱乐部喝啤酒时,我发现很少有人注册
val member1=NonEmptyFoo("member1","Foo_member1")
val member2=NonEmptyFoo("member2","Foo_member2")
val member3=NonEmptyFoo("member3","Foo_member3")
许多人后来登记,这对经理来说真的很好 第3步:为用户名和用户ID 创建单独的列表
val (memName,memId)=clubMembers map(x=>(x.bar1,x.bar2)) unzip
Whoooah!我的解决方案似乎有效,除非有一天经理来告诉我;
“伙计!人们非常兴奋,有时甚至没有输入数据就会注册;我无法获得正确的成员名单及其ID列表,因为列表中有许多空字段。这让我很紧张。
我保证俱乐部经理第二天就为他提供强大的解决方案。
我想到了两件事
的 1 即可。如果我从头开始编写代码,我需要删除旧代码,新代码可能无法读取
的 2 即可。我需要保持代码的可读性,并使其成为新开发人员可以轻松维护新代码并添加/删除功能的方式
晚上我想添加抽象。我添加了以下代码
第1步为具有有效条目的用户创建具有NonEmptyFoo的特征Foo,为具有无效条目的人创建具有EmptyFoo的特征
trait Foo
case class EmptyFoo() extends Foo
case class NonEmptyFoo(bar1:String,bar2:String) extends Foo
object Foo{
def apply(bar1:String,bar2:String)= (bar1,bar2) match{
case (x,y)=>if(x.isEmpty||y.isEmpty) EmptyFoo() else NonEmptyFoo(x,y)
case _=> EmptyFoo()
}
}
以上代码有好处。首先,我能够验证具有有效条目的用户和具有无效条目的用户。其次,我将空的非空Foo抽象为Foo,这样新的方法可以很容易地添加到Foo中,从而在子类中实现,从而隐藏了内部实现。
当用户输入
val mamber1=Foo("member1","Foo_member1")
工作表上的输出显示为
member1: Product with Serializable with Foo = NonEmptyFoo(member1,Foo_member1)
并且,当有人错过输入下面给出的字段之一时
val member2=Foo("member2","")
工作表上的输出显示为
member2: Product with Serializable with Foo = EmptyFoo()
有趣!!有用...
请耐心等待;
我能够执行抽象,并能够提出一个最小但很好的解决方案。但是,当我编写如下代码时,我遇到了真正的问题
val member1=Foo("member1","Foo_member1")
val member2=Foo("member2","Foo_member2")
val member3=Foo("member3","Foo_member3")
val clubMembers=List(member1,member2,member3)
//heres where the problem occurs
val (memName,memId)=clubMembers map(x=>(x.bar1,x.bar2)) unzip
Whooah!我被困在这里,因为编译器,在下面给出的lambda表达式中
x=>(x.bar1,x.bar2)
无法识别 bar1 和 bar2 。
但是,这个解决方案效果很好;
val member1=NonEmptyFoo("member1","Foo_member1")
val member2=NonEmptyFoo("member2","Foo_member2")
val member3=NonEmptyFoo("member3","Foo_member3")
val clubMembers=List(member1,member2,member3)
//heres where the problem occurs
val (memName,memId)=clubMembers map(x=>(x.bar1,x.bar2)) unzip
显然,解决方案违反了抽象,因为我明确地使用了内部类名,而我应该根据验证使用特征的名称,编译器应根据需要推断出对象的实例化需求。内部阶级。
请您说明新解决方案可能出现的问题。
由于编译器无法识别bar1和bar2的原因将受到高度赞赏。
是的确存在替代解决方案但我希望能够提出现有解决方案存在的问题,然后可以选择完全由您自行决定的替代解决方案。
提前感谢您的帮助!
答案 0 :(得分:1)
clubMembers
的推断类型为List[Foo]
(即使具体元素是NonEmptyFoo
的实例),而且Foo
没有bar1
且bar2
个字段,您无法在地图调用中访问它们。一种可能的解决方案是将bar1
和bar2
添加到Foo
:
sealed abstract class Foo(val bar1: String, val bar2: String)
case object EmptyFoo extends Foo("", "")
case class NonEmptyFoo(override val bar1: String, override val bar2: String) extends Foo(bar1, bar2)
object Foo {
def apply(bar1: String, bar2: String): Foo = {
if (bar1.isEmpty || bar2.isEmpty) EmptyFoo else NonEmptyFoo(bar1, bar2)
}
}