Аномалия перегрузки функции Scala
В Scala, почему эта перегрузка была разрешена?
class log {
def LogInfo(m: String, properties: Map[String, String]): Unit = {
println(m)
}
def LogInfo(m: String, properties: Map[String, String], c: UUID = null): Unit = {
println(m + c.toString())
}
}
Во втором определении функции LogInfo я установил для дополнительного параметра значение по умолчанию null. Когда я сделаю следующий вызов, он вызовет первую перегрузку.
val l: log = new log()
val props: Map[String, String] = Map("a" -> "1")
l.LogInfo("message", props)
Почему бы не бросить исключение? При значении по умолчанию я бы подумал, что оба определения могут выглядеть одинаково.
2 ответа
Исключение здесь не возникнет, потому что компилятор выбирает первую перегрузку как применимую. Это связано с тем, как разрешение перегрузки работает с аргументами по умолчанию. Согласно спецификации, сильный намек на то, что такие методы отбрасываются, будет следующей строкой:
В противном случае, пусть CC будет набором применимых альтернатив, которые не используют аргумент по умолчанию в приложении для
e1,…,em
,
Это связано с тем, как компилятор Scala генерирует байтовый код JVM для этих двух методов. Если мы скомпилируем их и посмотрим за кулисы, то увидим (для краткости опускаем фактический байт-код):
public class testing.ReadingFile$log$1 {
public void LogInfo(java.lang.String,
scala.collection.immutable.Map<java.lang.String, java.lang.String>);
Code:
public void LogInfo(java.lang.String,
scala.collection.immutable.Map<java.lang.String, java.lang.String>,
java.util.UUID);
Code:
public java.util.UUID LogInfo$default$3();
Code:
0: aconst_null
1: areturn
}
Вы видите, что сгенерированный код фактически испустил два метода, один из которых принимает два аргумента, а другой - три. Дополнительно, компилятор добавил дополнительный метод LogInfo$default$3
(имя на самом деле имеет значение, где $3
означает "параметр по умолчанию для третьего аргумента), который возвращает значение по умолчанию для c
переменная второй перегрузки. Если метод с аргументом по умолчанию должен быть вызван, LogInfo$default$3
будет использоваться для введения новой переменной с заданным значением.
Оба метода применимы, но перегрузка разрешения специально отбрасывает приложение, которое требует аргументов по умолчанию:
применимые альтернативы, которые не используют аргумент по умолчанию
http://www.scala-lang.org/files/archive/spec/2.12/06-expressions.html
Что касается "почему", представьте, что перегрузка имеет много параметров по умолчанию, так что большинство ее приложений не похожи на вызовы первого метода.