Please consider the following code:
trait A {
def foo(i: Int, j: String): Unit
}
class B extends A {
def foo(i0: Int, j0: String): Unit = ()
}
the implementing class B
has extended A
so it must implement foo
, but the parameter names are not the same as the parent (i -> i0, j -> j0). The result of this is that this now yields an error:
val b: B = new B
b.foo(i = 1, k = "") // ERROR!
b.foo(i0 = 1, k0 = "") // ok
In order to call the method with the original parameter names, B
's must be upcast to A
:
val b: B = new B
(b: A).foo(i = 1, k = "") // ok
I would think that in virtually every instance where an extending method changes the parameter names in an overriding method, this is done by accident, and probably most Scala users have never thought about happens when you change a parameter name in an override, and would have to test it out to see what happens (I know I did!), but when it does happen, it is impossible to correct without breaking source compatibility.
So I would propose that code like the above should emit a warning. Though, in some cases like for example when a method is overriding two super methods, it would be up for debate whether a warning should emitted, because the two super methods may disagree in parameter names:
trait A {
def foo(i: Int): Unit
}
trait B {
def foo(j: Int): Unit
}
class C extends A with B {
// up for debate if parameter name should be prescribed as `j`
// or if no warning is emitted
def foo(ij: Int): Unit
}
Precedence in Kotlin
Kotlin, having named parameters, does also emit warnings in this situation:
interface A {
fun foo(i: Int): Unit
}
class B: A {
override fun foo(j: Int) {}
//Warning:(86, 22) Kotlin: The corresponding parameter in the supertype 'A' is named 'i'.
//This may cause problems when calling this function with named arguments.
}
Example violations in the standard library:
Just to show that this corner case has caused problems, here are instances in the stdlib where parameters have been (probably accidentally) misaligned with their parent. Here’s a couple:
HashSet(1).contains(element = 0)
(HashSet(1): Set[Int]).contains(elem = 0)
// list.filter is actually a double whammy!
// List.filter takes p, which overrides
// collection.Iterable.filter which takes pred, which overrides
// collection.IterableOnceOps.filter, which takes p again
List(1).filter(p = _ > 0)
(List(1): collection.Iterable[Int]).filter(pred = _ > 0)
(List(1): collection.IterableOnceOps[Int, collection.Iterable, collection.Iterable[Int]]).filter(p = _ > 0)