5

How do I know if two objects of inner class have the same runtime type? In the example below I expect to see the class of aa.getClass == a.Inner and ba.getClass == b.Inner, but in fact they are both Outer.Inner and equal.

class Outer{
      class Inner{}
    }

    val a = new Outer
    val b = new Outer

    val aa = new a.Inner
    val ab = new a.Inner
    val ba = new b.Inner

    val res1 = aa.getClass == ba.getClass
    val res2 = aa.isInstanceOf[ab.type ]

    scala>      |      | defined class Outer

    scala> a: Outer = Outer@550a1967

    scala> b: Outer = Outer@5f9678e1

    scala> aa: a.Inner = Outer$Inner@70a36a66

    scala> ab: a.Inner = Outer$Inner@1dd6d4b7

    scala> ba: b.Inner = Outer$Inner@2e61d218

    scala> res1: Boolean = true

    scala> res2: Boolean = false

1 Answer 1

5

... aa.getClass == a.Inner and ba.getClass == b.Inner, but in fact they are both Outer.Inner and equal

This is not true. Inner is a class member and is unique to its parent instance of Outer. This means that both a and b have their own unique version of Inner, which are incompatible types. So a.Inner is not the same type as b.Inner, and therefore an a.Inner can never be equal to a b.Inner. I cannot assign one for the other:

scala> val z: a.Inner = aa       // aa is a.Inner, so this is ok
z: a.Inner = Outer$Inner@575d06dd

scala> val z: b.Inner = aa       // aa is not b.Inner, so it fails to compile
<console>:14: error: type mismatch;
 found   : a.Inner
 required: b.Inner
       val z: b.Inner = aa
                        ^

getClass just isn't very useful here.

We can prove this with reflection:

import scala.reflect.runtime.universe._

def tpeOf[A](a: A)(implicit tt: TypeTag[A]) = tt.tpe

scala> tpeOf(aa) =:= tpeOf(ba) // different Outer parents
res24: Boolean = false

scala> tpeOf(aa) =:= tpeOf(aa) // Same instance
res25: Boolean = true

scala> tpeOf(aa) =:= tpeOf(ab) // Same Outer parent
res26: Boolean = true

On the other hand, you can use Outer#Inner to specify that you don't care which Outer your Inner type belongs to.

val x: Outer#Inner = aa
val x: Outer#Inner = ab
val x: Outer#Inner = ba

So as stated by @BenReich, you could use aa.isInstanceOf[Outer#Inner] to check if you have any of those types, and they would all return true.

ab.type means something completely different. ab.type is a singleton type that contains nothing but ab. So naturally then, aa.isInstanceOf[ab.type] must be false, because aa is not ab, regardless of whether or not they are both a.Inner.

Sign up to request clarification or add additional context in comments.

4 Comments

Probably worth mentioning that they are both assignable to Outer#Inner
Thanks! Strange that we need to write a custom method to use when comparing inner class objects...
@Yegor Note that you can do ab.isInstanceOf[Outer#Inner] with the desired effect.
@Yegor My custom method is more so to make up for my lack of knowledge of the reflection library. There might be a method that already does it, but I just wanted a quick way to compare the types using what I already knew about TypeTags

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.