15

我有一个返回字符串的方法。我想用默认值替换它,例如"<empty>"它是否返回一个空字符串或null. 假设它的名字是getSomeString,这是一个昂贵的操作,所以我只能调用它一次,而且我不能将它的返回类型更改为Option[String]。目前,我正在执行以下操作:

val myStr = {
  val s = getSomeString
  if (s == null || s.isEmpty) "<empty>" else s
}

有没有更简单的方法来实现同样的事情?

4

6 回答 6

21
val myStr = Option(getSomeString).filterNot(_.isEmpty).getOrElse("<empty>")

更新

我发布这段代码是因为我认为这段代码的意图比 if/else 或模式匹配版本更清楚,但我没有考虑性能问题。

正如评论中的其他人所提到的,此代码比简单的 if / else 或模式匹配要慢得多(此行将创建很多新对象,这是一项昂贵的操作),因此请不要在性能问题时使用此代码。

于 2013-08-24T10:53:29.090 回答
14

给定一个昂贵的功能:

scala> def s(i: Int): String = i match { case 0=>null case 1=>"" case 2=>"hi" }
s: (i: Int)String

我认为这很容易阅读并且没有开销,请参见野外

scala> def q(i: Int) = s(i) match { case ""|null => "<empty>" case x => x }
q: (i: Int)String

scala> q(0)
res3: String = <empty>

scala> q(1)
res4: String = <empty>

scala> q(2)
res5: String = hi

在我看来,即使使用极简标点符号,这也不是那么富有表现力:

scala> Option(s(0)) filterNot (_.isEmpty) getOrElse "<empty>"
res6: String = <empty>

此外,对比anonfun闭包和附加方法调用的类成本:

scala> :javap -
  Size 1161 bytes
  MD5 checksum 765f5f67b0c574252b059c8adfab1cf0
  Compiled from "<console>"
[...]
         9: getstatic     #26                 // Field scala/Option$.MODULE$:Lscala/Option$;
        12: getstatic     #31                 // Field .MODULE$:L;
        15: iconst_0      
        16: invokevirtual #35                 // Method .s:(I)Ljava/lang/String;
        19: invokevirtual #39                 // Method scala/Option$.apply:(Ljava/lang/Object;)Lscala/Option;
        22: new           #41                 // class $anonfun$1
        25: dup           
        26: invokespecial #42                 // Method $anonfun$1."<init>":()V
        29: invokevirtual #48                 // Method scala/Option.filterNot:(Lscala/Function1;)Lscala/Option;
        32: new           #50                 // class $anonfun$2
        35: dup           
        36: invokespecial #51                 // Method $anonfun$2."<init>":()V
        39: invokevirtual #55                 // Method scala/Option.getOrElse:(Lscala/Function0;)Ljava/lang/Object;
        42: checkcast     #57                 // class java/lang/String
        45: putfield      #17                 // Field res6:Ljava/lang/String;

模式匹配通常只是一个 if-else,更小更快(即使考虑到它没有优化s == ""s.isEmpty):

scala> :javap -r #q
  public java.lang.String q(int);
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=5, args_size=2
         0: getstatic     #19                 // Field $line3/$read$$iw$$iw$.MODULE$:L$line3/$read$$iw$$iw$;
         3: iload_1       
         4: invokevirtual #22                 // Method $line3/$read$$iw$$iw$.s:(I)Ljava/lang/String;
         7: astore_3      
         8: ldc           #24                 // String 
        10: aload_3       
        11: invokevirtual #28                 // Method java/lang/Object.equals:(Ljava/lang/Object;)Z
        14: ifeq          22
        17: iconst_1      
        18: istore_2      
        19: goto          33
        22: aload_3       
        23: ifnonnull     31
        26: iconst_1      
        27: istore_2      
        28: goto          33
        31: iconst_0      
        32: istore_2      
        33: iload_2       
        34: ifeq          44
        37: ldc           #30                 // String <empty>
        39: astore        4
        41: goto          47
        44: aload_3       
        45: astore        4
        47: aload         4
        49: areturn       

但是受到另一个答案的启发,即使我永远不会把这段代码带回家见我的父母(因为"null"如果昂贵的函数返回它,它会错误地转换值——尽管它可能是这样做的一个特性),这里是一个正则表达式:

scala> def p(i: Int) = "" + s(i) replaceAll ("^null$|^$", "<empty>")
p: (i: Int)String

The"" + s(i)是 的简写String.valueOf,它当然会"null"为空引用值生成字符串。我很欣赏 SO 不仅能够快速生成问题的答案,而且能够鼓励一些开箱即用的思维。

于 2013-08-24T13:09:02.703 回答
2

您可以在第一步使用空字符串替换 null ,Option然后在结果为空时用默认文本替换(无论是因为它最初是空的还是因为它是空的):

Option(getSomeString).getOrElse("").replaceAll("^$","<empty>")
于 2013-08-24T11:21:02.237 回答
2

您可以添加一个方法来String使用隐式值类

object ImplicitClassContainer {
  implicit class RichString(val s: String) extends AnyVal {
    def getOrDefault(defaultValue: String): String = {
      s match {
        case null | "" => defaultValue
        case x => x
      }
    }
  }

像这样使用

import ImplicitClassContainer._

println("hi".getOrDefault("<empty1>"))

println("".getOrDefault("<empty2>"))

val s: String = null
println(s.getOrDefault("<empty3>"))

所以即使是方法调用null也得到了优雅的处理(Scala 2.10.1)。

于 2013-08-24T18:51:00.417 回答
2

如果您不需要更改:

Option(getSomeString).fold("<empty>")(s => s)

如果需要修改 if 为 nonEmpty 结果。简单的例子:

Option(getSomeString).fold("<empty>")(str => s"xxxxxxxx$str")
于 2018-04-11T13:11:05.777 回答
1
val myStr = getSomeString match { case ""|null => "<empty>" case s => s }
于 2014-02-20T16:11:41.367 回答