1761


Scalaの対話環境(REPL)で、クラス･トレイトの継承関係を確認する方法を探しています。

例えば、クラスA1とそれを継承したクラスA2があるとします。

[code]

    scala> class A1
    scala> class A2 extends A1
    
[/code]

このとき、以下のようにA2のインスタンスを作れば、A2はA1のサブクラスであることがわかります。

[code]

    scala> val a2 = new A2
    scala> a2.isInstanceOf[A1]
    res1: Boolean = true
    
[/code]

しかし、トレイトの場合はインスタンスが作れないので、このような方法では確認できません。

例えば、以下のようなときにトレイトB2とトレイトB1の継承関係を手軽に確認したいです。(いちいち抽象メンバを実装したりしたくない)

[code]

    scala> trait B1 {val i:Int}
    scala> trait B2 extends B1
    
[/code]

質問: 簡単にクラス･トレイトの継承関係を確認する方法はないでしょうか?


CC BY-SA 3.0
2014-12-16T12:35:55.180
1758
2014-12-16T14:18:22.750
2014-12-16T13:21:50.247
848
848
question
scala
Scalaで、継承関係の確認方法


[Reflection API](http://docs.scala-lang.org/ja/overviews/reflection/typetags-
manifests.html) を使うとランタイム型を取得できます。

[code]

    $ scala
    Welcome to Scala version 2.11.0 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_51).
    Type in expressions to have them evaluated.
    Type :help for more information.
    
    scala> import scala.reflect.runtime.universe._
    import scala.reflect.runtime.universe._
    
    scala> trait A1 {}
    defined trait A1
    
    scala> class A2 extends A1 {}
    defined class A2
    
    scala> def isA1[A: TypeTag](a: A): Boolean = typeOf[A] <:< typeOf[A1]
    isA1: [A](a: A)(implicit evidence$1: reflect.runtime.universe.TypeTag[A])Boolean
    
    scala> isA1(new A2)
    res0: Boolean = true
    
    scala> isA1(1)
    res1: Boolean = false
    
[/code]

**追記** :

Scala 2.9.x の場合は `Manifest` で似たことができますが、不正確な場合もあります。

[code]

    scala> import scala.reflect.Manifest
    import scala.reflect.Manifest
    
    scala> trait A1 {}
    defined trait A1
    
    scala> class A2 extends A1 {}
    defined class A2
    
    scala> def isApproxA1[A: Manifest](a: A) = manifest[A] <:< manifest[A1]
    isApproxA1: [A](a: A)(implicit evidence$1: scala.reflect.Manifest[A])Boolean
    
    scala> isApproxA1(new A2)
    res0: Boolean = true
    
    scala> isApproxA1(1)
    res1: Boolean = false
    
    scala> def isApproxSubType[A: Manifest, B: Manifest] = manifest[A] <:< manifest[B]
    isApproxSubType: [A, B](implicit evidence$1: scala.reflect.Manifest[A], implicit evidence$2: scala.reflect.Manifest[B])Boolean
    
    scala> trait A3 extends A1 {}
    defined trait A3
    
    scala> isApproxSubType[A3, A1]
    res3: Boolean = true
    
[/code]


CC BY-SA 3.0
2014-12-16T12:48:47.740
1761
2014-12-16T14:18:22.750
2014-12-16T14:18:22.750
3913
3913
1758
answer


無名クラスを利用すれば同様に確認できます。

[code]

    scala> class A1
    scala> trait T1 extends A1
    scala> val t = new T1 {}
    t: A1 with T1 = $anon$1@4b9e13df
    scala> t.isInstanceOf[A1]
    res0: Boolean = true
    
[/code]


CC BY-SA 3.0
2014-12-16T12:54:26.777
1763
2014-12-16T12:54:26.777
3916
1758
answer


Scalaのリフレクションはスレッドセーフじゃなかったりバイナリ互換なかったり実験的機能で安定してないので、ちゃんとJavaのリフレクションだけを使いましょう(というような回答を書けと言われました)

[code]

    object ReflectionUtil {
      private def getSuperClasses(clazz: Class[_]): List[Class[_]] = {
        @scala.annotation.tailrec
        def loop(o: Class[_], result: List[Class[_]]): List[Class[_]] = {
          val superClass = o.getSuperclass
          if (superClass == null) {
            result
          } else {
            loop(superClass, superClass :: result)
          }
        }
        loop(clazz, List(clazz))
      }
    
      def allSuperClassAndTrait(clazz: Class[_]): List[Class[_]] = {
        def loop(c: Class[_], result: List[Class[_]]): List[Class[_]] = {
          val interfaces = c.getInterfaces.toList
          if (interfaces.size == 0) {
            c :: result
          } else {
            interfaces.flatMap{ i => loop(i, i :: result) }
          }
        }
    
        val classes = for {
          c <- getSuperClasses(clazz)
          result <- loop(c, Nil)
        } yield result
    
        (classes.toSet - clazz).toList.sortBy(_.toString)
      }
    }
    
[/code]

[code]

    $ scala
    Welcome to Scala version 2.11.4 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_25).
    Type in expressions to have them evaluated.
    Type :help for more information.
    
    scala> :load ReflectionUtil.scala
    Loading ReflectionUtil.scala...
    warning: there were two feature warnings; re-run with -feature for details
    defined object ReflectionUtil
    
    scala> ReflectionUtil.allSuperClassAndTrait(classOf[List[_]]) foreach println
    class java.lang.Object
    interface java.io.Serializable
    interface scala.Equals
    interface scala.Function1
    interface scala.Immutable
    interface scala.PartialFunction
    interface scala.Product
    interface scala.collection.GenIterable
    interface scala.collection.GenIterableLike
    interface scala.collection.GenSeq
    interface scala.collection.GenSeqLike
    interface scala.collection.GenTraversable
    interface scala.collection.GenTraversableLike
    interface scala.collection.GenTraversableOnce
    interface scala.collection.Iterable
    interface scala.collection.IterableLike
    interface scala.collection.LinearSeq
    interface scala.collection.LinearSeqLike
    interface scala.collection.LinearSeqOptimized
    interface scala.collection.Parallelizable
    interface scala.collection.Seq
    interface scala.collection.SeqLike
    interface scala.collection.Traversable
    interface scala.collection.TraversableLike
    interface scala.collection.TraversableOnce
    interface scala.collection.generic.FilterMonadic
    interface scala.collection.generic.GenericTraversableTemplate
    interface scala.collection.generic.HasNewBuilder
    interface scala.collection.immutable.Iterable
    interface scala.collection.immutable.LinearSeq
    interface scala.collection.immutable.Seq
    interface scala.collection.immutable.Traversable
    
    scala> ReflectionUtil.allSuperClassAndTrait(classOf[List[_]]).contains(classOf[Seq[_]])
    res1: Boolean = true
    
[/code]


CC BY-SA 3.0
2014-12-16T13:14:39.350
1767
2014-12-16T13:14:39.350
56
1758
answer


Phantom Typeを使ってみてはどうでしょう？

[code]

    scala> trait B1 {val i:Int}
    defined trait B1
    scala> trait B2 extends B1
    defined trait B2
    scala> def hoge[T<:B1] = println("hello")
    hoge: [T <: B1]=> Unit
    scala> hoge[B2]
    hello
    
    scala> trait B0
    defined trait B0
    
    scala> hoge[B0]
    <console>:11: error: type arguments [B0] do not conform to method hoge's type parameter bounds [T <: B1]
                  hoge[B0]
                      ^
    
[/code]


CC BY-SA 3.0
2014-12-16T14:15:56.457
1785
2014-12-16T14:15:56.457
4011
1758
answer