知っててもそれほど頻繁には役にたたない(?)、トリビア的なもの。説明するよりコード示したほうがわかりやすい気がするので、まずはコード
Welcome to Scala version 2.10.3 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_45). Type in expressions to have them evaluated. Type :help for more information. scala> case class Foo(a: Int, b: String) defined class Foo scala> Foo.getClass.getSuperclass res0: Class[?0] forSome { type ?0 >: ?0; type ?0 <: Foo.type } = class scala.runtime.AbstractFunction2 scala> val f: (Int, String) => Foo = Foo f: (Int, String) => Foo = Foo scala> Foo eq f res1: Boolean = true
つまり、コンパニオンに以下のようなものが勝手に実装されるということです
case class Foo(a: Int, b: String) object Foo extends ((Int, String) => Foo){ override def apply(v1: Int, v2: String): Foo = new Foo(v1, v2) }
もちろん、「パラメータが2つのものがFunction2になる」というのに限らず、パラメータ1つでも、パラメータ3つでも同様*1です。
getClass.getSuperclass
もしくはgetClass.getInterfaces
をしてみるとわかりますが、自分が調べた限り少なくともScala2.7.7のころからずっと*2そういう仕様みたいです。
しかし、現状の仕様書を見てみたところ、この件に関する記述見当たりませんでした。*3
あと、本とかWebの資料でもこれに言及しているものあまり見たこと無いんですが、なんででしょうね*4。
メーリングリストや、ScalaのJIRAでコミッターの人が話してるのは見たことありますが。
これ知ってて何に役に立つの?と言われると微妙なのですが。たとえば、以下のようにプレースホルダー使ってわざわざ関数オブジェクトの生成をやり直したら、無駄なオブジェクト生成されて勿体無いという話です。
scala> val f = Foo(_, _) f: (Int, String) => Foo = <function2>
あと標題の、「型パラメータをとらない」ですが、ScalaのFunctionオブジェクトは、trait自体は型パラメータをとりますが、それぞれのFunctionNのapplyメソッドは型パラメータをとりません。なので、case class自体が型パラメータを取る場合*5は、そのコンパニオンがFunctionNを継承するのは不可能、という単純な(?)理由です。
ここからはだいぶ話変わるというか余談ですが
object creation impossible, since method apply in trait Function3 of type (中略) is not defined
という、case class定義しただけなのに、関係なさそうな一見謎なエラーメッセージに遭遇したのは
https://github.com/xuwei-k/scalikejdbc/commit/1429f9181d9#commitcomment-4800974
今まで説明したような「コンパニオンがFunctionNを継承する」という実装が、変な形で露呈してしまったという感じです。「コンパイルエラーにするべき」もしくは「コンパイル通るべきなのに、コンパイルエラーになってしまっている」けど、どちらにしろエラーメッセージがおかしいという感じかなぁ