在Scala中寻找异常和错误的非直观案例
在这篇文章中,我们与Nermin Serifovic和Andrew Phillips取得了联系 ,您可能会从Scala Puzzlers中了解到。 我们在一起选择了一些问题来探索Scala中的错误和异常。 有些问题乍一看似乎很怪异,但是当您深入挖掘背后的实际情况时,这一切都是有道理的。 我们希望这篇文章有助于您更好地了解Scala的来龙去脉,尽管如此,重要的是,您会发现它很有趣。 让我们深入。
答案在帖子的底部。 但是,嘿,别偷看!
1.异常失败
执行以下代码的结果是什么?
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Failure, Success}
val f = Future { throw new Error("fatal!") } recoverWith {
case err: Error => Future.successful("Ignoring error: " + err.getMessage)
}
f onComplete {
case Success(res) => println("Yay: " + res)
case Failure(e) => println("Oops: " + e.getMessage)
}
答案
- 印刷品:
Yay:忽略错误:致命! - 引发错误
- 印刷品:
糟糕!致命! - 印刷品:
糟糕:盒装错误
2. $!。*%迭代器!
执行以下代码的结果是什么?
val t = "this is a test"
val rx = " ".r
val m = rx.findAllIn(t)
println(m)
println(m.end)
println(rx.findAllIn(t).end)
答案
- 非空迭代器
5
5 - 空迭代器
java.lang.IllegalStateException:无匹配项
java.lang.IllegalStateException:无匹配项 - 非空迭代器
5
java.lang.IllegalStateException:无匹配项 - 非空迭代器
java.lang.IllegalStateException:无匹配项
java.lang.IllegalStateException:无匹配项
3.名字叫什么?
执行以下代码的结果是什么?
class C {
def sum(x: Int = 1, y: Int = 2): Int = x + y
}
class D extends C {
override def sum(y: Int = 3, x: Int = 4): Int = super.sum(x, y)
}
val d: D = new D
val c: C = d
c.sum(x = 0)
d.sum(x = 0)
答案
- 2
3 - 1个
3 - 4
3 - 3
3
4.(Ex)流惊喜
执行以下代码的结果是什么?
val nats: Stream[Int] = 1 #:: (nats map { _ + 1 })
val odds: Stream[Int] = 1 #:: (odds map { _ + 1 } filter { _ % 2 != 0 })
nats filter { _ % 2 != 0 } take 2 foreach println
odds take 2 foreach println
答案
- 印刷品:
1个
3 1个 3 - 印刷品:
1个
2 1个 3 - 第一条语句打印:
1个
3,第二个引发运行时异常 - 第一条语句引发运行时异常,第二条语句显示:
1个
3
5.弦的情况
执行以下代码的结果是什么?
def objFromJava: Object = "string"
def stringFromJava: String = null
def printLengthIfString(a: AnyRef) {
a match {
case s: String => println("String of length " + s.length)
case _ => println("Not a string")
}
}
printLengthIfString(objFromJava)
printLengthIfString(stringFromJava)
答案
- 印刷品:
不是字符串
长度为0的字符串 - 第一版画:
不是字符串,第二个则抛出NullPointerException - 印刷品:
长度为6的字符串
不是字符串 - 第一版画:
长度为6的字符串,第二个字符串引发NullPointerException
解决方案
1.异常失败
正确答案是: 4 =>此代码段将显示“糟糕:装箱错误”。
val f = Future { throw new Error("fatal!") } recoverWith {
case err: Error => Future.successful("Ignoring error: " + err.getMessage)
}
f onComplete {
case Success(res) => println("Yay: " + res)
case Failure(e) => println("Oops: " + e.getMessage)
}
那我们这里有什么?
有一个Future消息“致命!”抛出错误,您可能天真地认为它可以恢复并以成功状态结束。 事实并非如此。 这里抛出的是一个异常而不是一个错误,因此我们不在recoverWith块中输入err大小写。
既然我们知道未来会失败,那么还有另一个警告。 我们打印出的消息不是我们抛出的错误消息。 抛出的异常是一个java.util.concurrent.ExecutionException,当从一个失败的Future创建它时,它带有一个“ Boxed Error”消息。
因此,未来的结果是:
失败(新的ExecutionException(“装箱的错误”,新的错误(“致命!”))))
2. $!。*%迭代器!
正确答案是: 3 =>
非空迭代器
5
java.lang.IllegalStateException:无匹配项
val t = "this is a test"
val rx = " ".r
val m = rx.findAllIn(t)
println(m)
println(m.end)
println(rx.findAllIn(t).end)
那我们这里有什么?
此代码段创建一个正则表达式,并在提供的字符串中查找其所有匹配项(空格字符)。 很有道理,但是findAllIn返回一个Iterator。 当打印时,它的toString给我们“空迭代器”或“非空迭代器”。 由于我们对该字符串有3次匹配,因此它不是空的。
向前,end返回当前命中结束后字符的索引。 因此,它是第一个空格之后的索引,即5。
现在出现了令人惊讶的部分。 最后,我们得到一个IllegalStateException。 确实$!。*%迭代器! 当涉及到业务时,除非您要求或检查第一个元素,否则不会激活正则表达式。 对于第一个println,我们要求使用toString,并且作为“副作用”,我们激活了正则表达式,因此第二个println没有问题。 对于第三个println,我们被悬吊起来并被异常击中。
3.名字叫什么?
正确答案是: 3 =>打印4、3
class C {
def sum(x: Int = 1, y: Int = 2): Int = x + y
}
class D extends C {
override def sum(y: Int = 3, x: Int = 4): Int = super.sum(x, y)
}
val d: D = new D
val c: C = d
c.sum(x = 0)
d.sum(x = 0)
那我们这里有什么?
有两个问题:
- 参数名称的绑定由编译器完成,并且编译器可以使用的唯一信息是变量的静态类型。
- 对于具有默认值的参数,编译器将创建计算默认参数表达式的方法。 在上面的示例中,类C和D都包含两个默认参数的方法sum $ default $ 1和sum $ default $ 2。 当缺少参数时,编译器将使用这些方法的结果,并在运行时调用这些方法。
从c.sum(x = 0)开始,我们使用缺少y参数的C类和。 这是第二个参数。 幕后发生的事情,因为c是从d创建的,它带有为d类中缺少的参数创建的默认方法。 当选择使用哪种方法时,无论x和y的名称相反,它都会执行此操作。 第二个参数丢失,因此假定它是4,而第一个参数是0。第一个结果是0 + 4 = 4。 哇,我的大脑很痛。
对于d.sum(x = 0),情况有些不同,我们直接求D的和,而第一个y参数丢失。 我们假设它是3,结果是0 + 3。
Josh Suereth用以下规则总结了这一点:“名称是静态的; 值就是运行时间”。
4.(Ex)流惊喜
正确答案是: 3 =>第一条语句输出:1 3,第二条语句抛出运行时异常
val nats: Stream[Int] = 1 #:: (nats map { _ + 1 })
val odds: Stream[Int] = 1 #:: (odds map { _ + 1 } filter { _ % 2 != 0 })
nats filter { _ % 2 != 0 } take 2 foreach println
odds take 2 foreach println
那我们这里有什么?
此代码段创建2个流,小数和赔率。 第一个包含(1 ,?)并保存向其添加更多项的规则,简单+1递增。 同样,第二个规则与过滤器具有相同的规则,该过滤器仅允许保留奇数。 当我们尝试打印时,麻烦就来了。
对于nat,我们展开时没有问题,我们只取前2个奇数值,因为在延迟创建流之后应用了过滤器:(1,(2,(3,?)))
赔率是,我们真的不能拿2。只是第一个。 我们进入无尽的递归状态。 由于2不是奇数,并且在使用+1规则创建Stream时立即激活了过滤器,所以我们永远不会达到3并遇到运行时异常。
5.弦的情况
正确答案是: 3 =>打印:长度为6的字符串,不是字符串
def objFromJava: Object = "string"
def stringFromJava: String = null
def printLengthIfString(a: AnyRef) {
a match {
case s: String => println("String of length " + s.length)
case _ => println("Not a string")
}
}
printLengthIfString(objFromJava)
printLengthIfString(stringFromJava)
那我们这里有什么?
对于objFromJava,即使它最初是作为对象创建的,我们也要处理它。 由于模式匹配分辨率基于运行时类型,因此将打印出字符串“ string”的长度。
对于stringFromJava(为null),我们得到的是“非字符串”。 由于null是完全不同的问题,因此我们需要明确检查它是否是其中一种情况,并给予一些特殊的照顾。
资料来源
- 异常失败
- $!。*%迭代器!
- 名字叫什么?
- (Ex)流惊喜
- 一串弦
访问Scala Puzzlers以查看完整列表。
最后的想法
再次感谢Nermin和Andrew与我们分享他们的益智游戏! 我们希望您喜欢阅读它们并尝试解决它们。 在撰写本文时,我们当然已经学到了一些新知识,希望您也是如此。 尽管如果您曾经在自己的代码中遇到过这样的难题,那可能就没那么有趣了。 对于这种情况,我们为Scala构建了Takipi 。 Takipi是一个本地代理,它知道如何跟踪未捕获的异常,捕获的异常以及在生产中的服务器上记录错误。 它使您可以查看导致错误的变量值(遍及整个堆栈),并将其覆盖在代码中。
翻译自: https://www.javacodegeeks.com/2015/11/5-scala-puzzlers-will-make-brain-hurt.html