本篇博客中我们将采用类似的方法,并熟悉Scala编程语言的另一个重要特性模式匹配。同样我们将通过编写一些简短的代码片段,一系列小步骤来逐步深入。 我们首先声明一个非常简单的case类,后面将对其详细剖析caseclassFullName(first:String,last:String) case类的许多其他有用特性(例如结构化equals、hashCode、copy和toString)中,Scala编译器支持以下代码valmeFullName(Linas,Medinas) valFullName(meFirst,meLast)me meFirst:StringLinas meLast:StringMedinas 请注意这里的一个很好的对称性:构造时me在左侧,带有两个字符串参数的FullName(。。。)在赋值的右侧,解构时正好相反。 当谈到Scala模式匹配时,首先想到的是match语句(它类似于许多其他编程语言中的switchcase,但是更强大)。可以在Scala中的很多地方可以使用模式匹配:你可以在定义lambda函数时使用它,也可以在forcomprehension生成器的左侧,甚至在上面例子中的赋值语句中。为简单起见,在本文的其余部分,我们将主要在赋值语句中使用模式匹配。 现在我们已经定义了case类以及一些使用它的代码,接着尝试了解Scalacase类的特别之处以及如何使用相关代码。有时理解某事物如何工作的一个非常好的方法是破坏它,然后尝试使其再次工作!先将FullName类定义的case关键字排除caseclassFullName(first:String,last:String) 如果尝试上述代码,会发现代码(valueme的构建和它的解构)编译报错。为了修复它,我们需要在事情开始崩溃之前手动实现Scala编译器之前提供给我们的功能,我们为FullName类添加一个伴随对象objectFullName{ defapply(first:String,last:String):FullName newFullName(first,last) defunapply(full:FullName):Some〔(String,String)〕 Some((full。first,full。last)) } Scala中的伴生对象是一个单例,与它的伴生类同名且在同一个文件中。而且伴随对象和它的类可以访问彼此的私有成员。伴生对象是放置类的静态成员的地方(与Java不同,Scala没有static修饰符),这提供了更清晰的静态实例成员分离。 注意:我们必须稍微更改FullName类定义,以使FullName。unapply编译成功caseclassFullName(valfirst:String,vallast:String) 如果不进行修改,first和last只会作为构造函数的参数,无法通过unapply访问它们。在first和last之前添加val会将它们同时转换为构造函数参数和实例字段(默认为public)。在我们删除case关键字之前Scala编译器会自动为我们生成此功能以及伴随对象。 现在手动添加所有这些代码可以修复编译问题,继续让我们深入了解刚刚实现的两个方法的细节defapply(first:String,last:String):FullName apply是Scala中的一个特殊方法名称,按照约定可以在代码中省略,所以FullName(。。。)等价于FullName。apply(。。。),我们正在使用它来构造FullName的新实例,而无需new关键字。defunapply(full:FullName):Some〔(String,String)〕 unapply正好相反它解构了一个FullName的实例,并且是模式匹配的基础,接下来我们将重点介绍这种方法,在这种情况下,它将FullName解构为两个字符串值,并将它们包装在Some中,这意味着它可以匹配FullName的任何实例(稍后我们将探讨部分匹配partialmatching)。 再次注意这两个方法的对称性:apply将两个字符串作为参数,并返回一个FullName的实例。而unapply则恰好相反。 现在我们对什么是unapply以及它如何用于解构模式匹配有了一个非常基本的了解。在大多数情况下,它已经由Scala处理unapply的实现不仅为我们编写的所有case类提供,而且为几乎所有Scala标准库中的所有内容提供,包括集合(如果适用),事实上实现自己的unapply并不常见,除非你是某个有趣库的开发者,然而我们可以作弊在Java中unapply肯定不存在,让我们从java。time中获取一些类,并在它们上添加对Scala模式匹配的支持importjava。time。{LocalDate,LocalDateTime,LocalTime} 能够将Date分解为年、月和日,将Time分解为小时、分钟和秒,这很自然。此外DateTime转换为日期和时间,根据我们已有的知识,这非常简单。但是我们不能使用名称LocalDate、LocalDateTime和LocalTime来创建合适的伴生对象,因为伴生对象需要与对应的类放在相同的文件,但由于这些类来自Java标准库,因此不可能。为了避免名称冲突,我们简单地将实现对象的名称中省略LocalobjectDateTime{ defunapply(dt:LocalDateTime):Some〔(LocalDate,LocalTime)〕 Some((dt。toLocalDate,dt。toLocalTime)) } objectDate{ defunapply(d:LocalDate):Some〔(Int,Int,Int)〕 Some((d。getYear,d。getMonthValue,d。getDayOfMonth)) } objectTime{ defunapply(t:LocalTime):Some〔(Int,Int,Int)〕 Some((t。getHour,t。getMinute,t。getSecond)) } 接着使用它们:valDate(year,month,day)LocalDate。now valTime(hour,minute,second)LocalTime。now LocalDate和LocalTime都按照预期被解构为3个Int值。如果我们只需要一些解构的值而不需要其他值,可以使用下划线代替那些不需要的值valDate(,month,day)LocalDate。now 一个更有趣的例子是LocalDateTime的嵌套解构valDateTime(Date(y,m,d),Time(h,mm,s))LocalDateTime。now 这为我们提供了6个Int值(日期部分为3,时间部分为3)。 模式匹配的另一个非常有用的特性是整个值的赋值,这可以在解构之外完成。对于我们的DateTime示例,它可能如下所示valdtDateTime(dateDate(y,m,d),timeTime(h,mm,s)) LocalDateTime。now 除了6个Int值,还得到一个LocalDate值,一个是LocalTime值,最后是LocalDateTime的整个值(以dt为单位)。 在上面的所有示例中,我们都解构为固定数量的值(年、月、日)、或(时、分、秒)或(日期、时间)。在某些情况下我们需要处理一系列值,而不是某些固定数量的值,可以尝试通过将LocalDateTime解构为一系列IntobjectDateTimeSeq{ defunapplySeq(dt:LocalDateTime):Some〔Seq〔Int〕〕 Some(Seq( dt。getYear,dt。getMonthValue,dt。getDayOfMonth, dt。getHour,dt。getMinute,dt。getSecond)) } unapplySeq是unapply的变体,它解构为一系列值而不是固定大小的元组。在这个例子中,序列的长度总是6,但可以省略它的尾部,因为不需要它valDateTimeSeq(year,month,day,hour,)LocalDateTime。now 是Scalavarargs的语法 到现在为止,unapplyunapplySeq总是返回Some。为此unapply将返回Some以防该值符合某些条件,而None则不符合。我们已经在处理LocalTime的值,将它们匹配到AM或PM时间将是一个自然的例子objectAM{ defunapply(t:LocalTime):Option〔(Int,Int,Int)〕 tmatch{ caseTime(h,m,s)ifh12Some((h,m,s)) caseNone } } objectPM{ defunapply(t:LocalTime):Option〔(Int,Int,Int)〕 tmatch{ caseTime(12,m,s)Some(12,m,s) caseTime(h,m,s)ifh12Some(h12,m,s) caseNone } } 其中case是默认情况,如果没有其他匹配项,则会使用此进行匹配,此外我们刚刚介绍了另外两个用于部分匹配的功能 守卫(guards),例如caseTime(h,m,s)ifh12常量匹配,例如caseTime(12,m,s) 现在已经看到Scala模式匹配的强大功能! 我们自己实现一个可以很好地格式化当前时间的时钟,通过使用模式匹配和AMPM提取器(加上一些看起来像表情符号流的老派Java字符串格式)LocalTime。nowmatch{ casetAM(h,m,) fh2d:m02dAM(tprecisely) casetPM(h,m,) fh2d:m02dPM(tprecisely) } 我们已经探索了Scala模式匹配的大部分特性。可以在这里〔1〕找到这篇博文的所有源代码,为了更好地理解可以在IntelliJIDEA中运行这些代码,最后如果Scala代码中有一些复杂的、嵌套的ifs和elses,请尝试使用模式匹配来更好地重构它。 引用链接 〔1〕这里:https:gist。github。comlinasm003eec9eacc641167227193f5879bbd9