我刚开始使用scala,我正在将一些java代码转换为scala,并尝试使其更好,功能更优雅。
我有以下代码,其中包含一个方法(getRequiredUploadPathKeys),它提供了MyClass中所有可用路径模板所需的所有路径键的并集。
trait PathTemplate {
def getRequiredPathKeys:Set[PathKey]
}
class MyClass(accessPaths:Set[PathTemplate], targetPath:PathTemplate){
def getAllRequiredPathKeys: Set[PathKey] = {
val pathKeys = HashSet[PathKey]()
pathKeys.addAll(targetPath.getRequiredPathKeys)
for (accessTemp <- accessPaths) {
pathKeys.addAll(accessTemp.getRequiredPathKeys)
}
return pathKeys
}
}
这个方法似乎在scala中更加简洁。任何人都可以指出我如何去那里吗?
谢谢, 保罗
答案 0 :(得分:6)
def getAllRequiredPathKeys =
(accessPaths + targetPath).flatMap(_.getRequiredPathKeys)
或者用于
def getAllRequiredPathKeys =
for(path <- accessPaths + targetPath;
key <- path.getRequiredPathKeys) yield key
答案 1 :(得分:6)
这里奇怪的是你正在使用 Java的 HashSet
- Scala中没有addAll
- 并且不需要它。
所以我将评论各种设计决策以及它们与Java到Scala的区别。首先,
val pathKeys = HashSet[PathKey]()
惯用语Scala通常不会引用实现接口的类,因为接口有一个工厂。所以你通常只使用Set[PathKey]()
。此外,这显然是Scala工厂,因为没有new
关键字 - 这意味着其余的代码不起作用,因为Scala的addAll
中没有Set
。
pathKeys.addAll(targetPath.getRequiredPathKeys)
这里你不直接使用targetPath.getRequiredPathKeys
,因为Java的Set
是可变的。默认的Scala Set
是不可变的,这使得它无用 - 您只需使用targetPath.getRequirePathKeys
作为基本集,而无需将其元素添加到另一个集合。
性能方面,类似Scala的函数式语言 - 在其不可变集合实现中使用 persistent datastructures 。这意味着它重用一个数据结构的一部分来创建派生数据结构,而不是每次创建新数据结构时都复制所有内容。这是唯一可能的,因为不变性保证。
无论如何,你可以这样做:
val pathKeys = targetPath.getRequiredPathKeys
接下来,
for (accessTemp <- accessPaths) {
pathKeys.addAll(accessTemp.getRequiredPathKeys)
}
首先,理解的惯用用法是返回一个值,即for-yield。上述用法应仅限于代码的副作用部分。更清楚的是,上述步骤可以分两步完成:
// Get all required Path Keys
val requiredPathKeys = for {
accessPath <- accessPaths
requiredPathKey <- accessPath.getRequiredPathKeys
} yield requiredPathKey
// Then add them
pathKeys.addAll(requiredPathKeys)
但是,鉴于您没有addAll
填写Scala的Set
,您需要将pathKeys
设为var
,或使用可变数据结构,或只是改变顺序:
val requiredPathKeys = for {
accessPath <- accessPaths
requiredPathKey <- accessPath.getRequiredPathKeys
} yield requiredPathKey
val pathKeys = targetPath.getRequiredPathKeys ++ requiredPathKeys
但请注意getRequiredPathKeys
的重复。功能程序员厌恶重复。在我看来,有时过于如此,但在这种情况下,它可以很容易地删除:
val pathKeys = for {
accessPath <- accessPaths + targetPath
requiredPathKey <- accessPath.getRequiredPathKeys
} yield requiredPathKey
上述代码表明了另一项改进。如果您看到this question有关Scala的理解,您会发现以上内容相当于
val pathKeys = (accessPaths + targetPath).flatMap( accessPath =>
accessPath.getRequiredPathKeys.map( requiredPathKey => requiredPathKey ))
最后的map
是多余的,因为它没有做任何事情。实际上,只要yield
只返回一个简单的标识符,表达式中就会有一个冗余的map
。删除它给我们:
val pathKeys = (accessPaths + targetPath).flatMap(accessPath =>
accessPath.getRequiredPathKeys)
或者,使用Scala的匿名函数参数语法
val pathKeys = (accessPaths + targetPath).flatMap(_.getRequiredPathKeys)
最后,
return pathKeys
Scala中的关键字return
用于指定工作流中的异常 - 该方法的执行被中止而不是一直到最后。并非这不是没有用,但是,就像例外本身一样,它不应该没有原因使用。在这里,您应该使用
pathKeys
但是,此时,您的代码只是这两行:
val pathKeys = (accessPaths + targetPath).flatMap(_.getRequiredPathKeys)
pathKeys
这使得val
任务完全减少了。因此,您可以将其减少为:
(accessPaths + targetPath).flatMap(_.getRequiredPathKeys)
因此方法变为
def getAllRequiredPathKeys: Set[PathKey] = {
(accessPaths + targetPath).flatMap(_.getRequiredPathKeys)
}
现在,我知道我说“终于”回到了那里,但是还有一个Scala惯例可以应用。每当一个方法包含单个表达式而不是多个语句时,约定就是省略大括号。换句话说,这样做:
def getAllRequiredPathKeys: Set[PathKey] =
(accessPaths + targetPath).flatMap(_.getRequiredPathKeys)
或者,如果空间允许,将所有内容放在一行上。实际上,如果你删除了getAllRequiredPathKeys
类型,那么它就很合适了。另一方面,鼓励使用公共方法的显式返回类型。
因此,这将为您提供Scala的不可变Set
。假设您的输入和输出必须是Java的Set
。在这种情况下,我没有看到任何可以简化代码的事情,除非你愿意在Java和Scala集之间进行转换。你可以这样做:
trait PathTemplate {
def getRequiredPathKeys:java.util.Set[PathKey]
}
import scala.collection.JavaConverters._
class MyClass(accessPaths:java.util.Set[PathTemplate], targetPath:PathTemplate){
def getAllRequiredPathKeys: java.util.Set[PathKey] =
(accessPaths.asScala + targetPath).flatMap(_.getRequiredPathKeys.asScala).asJava
}
然而,这使用了Scala的 mutable Set
,这意味着创建新Set
的每个操作都将复制旧Set
的所有内容。< / p>
答案 2 :(得分:4)
你的意思是:
def getAllRequiredPathKeys = (accessPaths map { _.getRequiredPathKeys }).flatten ++ targetPath.getRequiredPathKeys