简体   繁体   中英

How to use instanceof with scala object?

I've the following scala hierarchy:

sealed trait SessionResult[+T] {
  def toOption: Option[T]
}

object SessionResult {
  trait SessionValue[T] extends SessionResult[T] {
    def session: T
    def toOption: Option[T] = Some(session)
  }
  trait NoSessionValue[T] extends SessionResult[T] {
    def toOption: Option[T] = None
  }

  case class Decoded[T](session: T) extends SessionResult[T] with SessionValue[T]
  case class CreatedFromToken[T](session: T) extends SessionResult[T] with SessionValue[T]

  case object NoSession extends SessionResult[Nothing] with NoSessionValue[Nothing]
  case object TokenNotFound extends SessionResult[Nothing] with NoSessionValue[Nothing]
  case object Expired extends SessionResult[Nothing] with NoSessionValue[Nothing]
  case class Corrupt(e: Exception) extends SessionResult[Nothing] with NoSessionValue[Nothing]
}

But I use this code from java and the following piece of code does not compile:

SessionResult<SomeSession> sr = ...
System.out.println(sr instanceof NoSession)

Why? And also how can I use instanceof to check the class of scala's object?

The error I'm getting is:

Inconvertible types; cannot cast SessionResult<SomeSession> to NoSession.

The problem lies in the fact that you're putting a hard bound on the generic parameter - NoSession is a SessionResult[Nothing] .

So (in Java parlance) the only compatible variant of SessionResult<T> compatible to SessionResult.NoSession$ can be SessionResult<Nothing$> .

ie this will compile

public SessionResult<Nothing$> test() {

    return null;
}

public void blah() {
    if(test() instanceof SessionResult.NoSession$) {
    }
}

while eg this won't

public <T> SessionResult<T> test() {

    return null;
}

public void blah() {
    if(test() instanceof SessionResult.NoSession$) {
    }
}

Fortunately, since NoSession is an object , hence you can just reference-test the singleton value:

SessionResult.NoSession$.MODULE$.equals(test());

( equals is required as due to the variance you need upcast to Object - you can do that manually, but equals saves you some time on that)


Alternatively, you can just selectively wildcard the generic parameter, ie:

public static SessionResult<?> testYay() {
    return SessionResult.NoSession$.MODULE$;
}

public static SessionResult<?> testNay1() {
    return null;
}

public static SessionResult<?> testNay2() {
    return SessionResult.Expired$.MODULE$;
}

public static <T> SessionResult<T> testNay3() {
    return null;
}

public static void blah() {
    //prints true
    System.out.println(testYay() instanceof SessionResult.NoSession$);
    //prints false
    System.out.println(testNay1() instanceof SessionResult.NoSession$);
    //prints false
    System.out.println(testNay2() instanceof SessionResult.NoSession$);
    //prints false (and compiles)
    System.out.println((SessionResult<?>) testNay3() instanceof SessionResult.NoSession$);
}

This is a very hacky solution, but probably the most convenient for code that mostly deals with such equality checks in Java. As demonstrated in the testNay3 , you can limit the "collateral damage" of using generic types this way via simple in-place casts.

EDIT: changed to wildcard as per Alexey's hint.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM