Это - неприятное нарушение безопасности типов в моем проекте, таким образом, я ищу способ отключить его. Кажется этим, если функция берет AnyRef (или java.lang. Объект), можно вызвать функцию с любой комбинацией параметров, и Scala объединит параметры в Кортеж, возражают и вызывают функцию.
В моем случае функция не ожидает Кортеж и перестала работать во времени выполнения. Я ожидал бы, что эта ситуация будет поймана во время компиляции.
object WhyTuple {
def main(args: Array[String]): Unit = {
fooIt("foo", "bar")
}
def fooIt(o: AnyRef) {
println(o.toString)
}
}
Вывод:
(foo,bar)
Компилятор способен интерпретировать методы без круглых скобок. Поэтому он воспринимает круглые скобки в fooIt как Tuple. Ваш вызов будет таким же, как:
fooIt( ("foo","bar") )
Тем не менее, вы можете заставить метод исключить вызов, и получить значение, если вы используете какую-нибудь обертку, например Some(AnyRef) или Tuple1(AnyRef).
Я думаю, что определение (x, y) в Predef является ответственным. Флаг компилятора "-Yno-Prefs" может быть полезен, если вы готовы выполнить работу по ручному импорту любых неявных выражений, которые могут вам понадобиться. Под этим я подразумеваю, что вам придется добавлять import scala.Predef._ повсюду.
Никаких имплицитов или Predef здесь нет - просто старая добрая магия компилятора. Вы можете найти его в средстве проверки типов . Я не могу найти его в спецификации прямо сейчас.
Если вы достаточно мотивированы, вы можете добавить к компилятору параметр -X, чтобы предотвратить это.
В качестве альтернативы, вы можете избежать написания методов arity-1, которые принимают супертип TupleN
.
Редактировать : По мнению людей, более информированных, чем я, следующий ответ на самом деле неверен: см. этот ответ . Спасибо Аарону Новструпу за указание на это.
Это на самом деле причуда парсера , а не системы типов или компилятора. Scala позволяет вызывать функции с нулевым или одним аргументом без скобок, но не с более чем одним аргументом. Итак, как Фред Хаслам говорит , то, что вы написали , не является вызовом с двумя аргументами, это вызов с одним аргументом, имеющим значение кортежа. Однако, если метод действительно принимает два аргумента, вызов будет вызовом с двумя аргументами.Похоже, что смысл кода влияет на то, как он разбирается (что немного отстойно).
Что касается того, что вы можете с этим поделать, это непросто. Если метод действительно требует двух аргументов, эта проблема исчезнет (т.е. если кто-то по ошибке попытается вызвать его с одним аргументом или с тремя, он получит ошибку компиляции, как вы ожидаете). Не думаете ли вы, что есть какой-то дополнительный параметр, который вы откладываете добавить к этому методу? :)
Как насчет чего-то вроде этого:
object Qx2 {
@deprecated def callingWithATupleProducesAWarning(a: Product) = 2
def callingWithATupleProducesAWarning(a: Any) = 3
}
Кортежи имеют признак Product, поэтому любой вызов callWithATupleProducesAWarning, передающий кортеж, будет выдавать предупреждение об устаревании.
Не могли бы вы также добавить двухпараметрическое переопределение, которое помешало бы компилятору применять синтаксический сахар? Если сделать типы, принимающие подходящим образом затемненными, вы вряд ли получите ложные срабатывания. Например:
object WhyTuple {
...
class DummyType
def fooIt(a: DummyType, b: DummyType) {
throw new UnsupportedOperationException("Dummy function - should not be called")
}
}