Ответ 1
Чтобы ответить на ваш вопрос, нам нужно взглянуть на то, что происходит, когда компилятор Scala должен выполнить перегрузочное разрешение. Это описано в SLS 6.23.3 (для Scala 2.9).
Возьмем несколько более простой вариант вашего примера:
object Test {
def apply[T](x1: T) = "one arg" // A
def apply[T](x1: T, x2: T) = "two args" // B
def apply[T](elems: T*) = "var-args: " + elems.size // C
}
И посмотрите на эти три вызова:
Test(1) // fails, ambiguous reference, A and C both match arguments
Test[Int](1) // returns "one arg"
Test(1,2) // returns "two args", not "var-args: 2"
Начнем с первого. Во-первых, компилятор рассматривает форму каждого аргумента, который является типом, который описывает, в основном, если аргумент является значением или функцией. Здесь нет трудностей, 1
- очень нормальное, скучное значение, а его форма - тип Nothing
.
Теперь он имеет единственный аргумент 1
, тип Nothing
и находит все альтернативы, которые применимы к нему. Он находит два из них:
-
apply[T](x1: T)
: он принимает один аргумент неограниченного типа, поэтому он может принимать аргумент типаNothing
, -
apply[T](elems: T*)
: он может быть применен к любому числу (0
включен) аргументов одного и того же неограниченного типа, поэтому он может получить один элемент типаNothing
.
Это был только один, он остановился бы там и выберет тот.
Второй шаг идентичен приведенному выше, за исключением того, что он набирает каждый аргумент с ожидаемым типом undefined. В основном здесь он смотрит на две альтернативы слева и выясняет, применимы ли они к аргументу 1
типа A <: Int
. Не повезло, они оба.
Если вы были двумя изменениями apply[T](x1: T)
до apply(x1: String)
и оставите один в одиночку, здесь будет только одна применимая альтернатива влево, и она будет успешной и остановится.
Затем компилятор вычисляет relative weight
каждой альтернативы слева друг от друга. SLS заявляет, что
Относительный вес альтернативы A над альтернативой B является числом от 0 до 2, определяемой как сумма
- 1, если A является таким же конкретным, как B, 0 в противном случае, и
- 1, если A определен в классе или объекте, который является производным от класса или объекта определяя B, 0 в противном случае.
В этот момент должна быть одна альтернатива, которая имеет более высокий балл, чем все остальные, или есть ошибка двусмысленности. Мы можем игнорировать "определенную" часть, они определены в одном и том же месте.
-
A
является таким же конкретным, какC
, потому что вы всегда можете вызватьC
с единственным аргументомA
, -
C
определяется какA
из-за вывода типа: вы всегда можете вызыватьA
с аргументомC
, так какA
может принимать что угодно (его тип параметра может быть выведен на все, что мы хотим). ПараметрыC
рассматриваются какSeq[A]
, поэтомуT
выводится какSeq[A]
вA
, и он может это назвать. Таким образом,C
является таким же конкретным, какA
.
Это можно увидеть, если вы измените A
на apply[T <: Int](x: T)
: он полностью подходит к поиску наиболее специфического, но этот вывод типа времени не может найти способ сделать A
применимым к C
аргумент (a Seq
), потому что он не является подтипом Int
, поэтому A
является наиболее конкретным. Очевидно, что то же самое происходит, если вы меняете A
на apply(x: Int)
, вывод типа даже не может сделать ничего.
Это также объясняет, почему Test[Int](1)
удается вызвать версию одного аргумента. Две конечные альтернативы идентичны, но параметр A
type привязан к Int
, а вывод типа не может изменить его для соответствия аргументу C
.
Наконец, применение той же логики дает вам, почему Test(1,2)
отлично работает:
-
B
имеет значениеC
: вы всегда можете вызыватьC
с аргументамиB
, - но
C
не, какB
: никакому типу вывода не удастся установить одинSeq
в метод, который принимает два параметра.
Итак, apply[T](x1: T, x2: T)
является наиболее специфичным и ошибок нет.
В основном для var-arg и обычного метода для получения двусмысленности вам нужно иметь одинаковое количество аргументов и способ обхода типа вывода (по крайней мере) последнего аргумента:
def apply[T](x1: T)
def apply[T](x: T*)
или
def apply[T](x1: Int, x2:T)
def apply[T](x1: Int, x: T*)
И так далее...
Изменить. Сначала я не был уверен, будет ли повторяющийся параметр рассматриваться как Seq[A]
или TupleX[...]
при поиске специфичности. Это определенно не кортеж, и авто-tupling не имеет ничего общего с этим.