3

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.
Opal
  • 81,889
  • 28
  • 189
  • 210
  • why exactly does it not compile? i would guess, because you already know that `sr instanceof SessionResult` – XtremeBaumer Jul 20 '17 at 13:43
  • @XtremeBaumer I get: _Inconvertible types; cannot cast SessionResult to NoSession_. – Opal Jul 20 '17 at 13:46
  • then look at what i said next in my previous comment. i am pretty sure that is the reason – XtremeBaumer Jul 20 '17 at 13:51
  • @XtremeBaumer but I need to get the particular type. – Opal Jul 20 '17 at 13:52
  • try something like `sr.get(0) instanceof NoSession` or `sr instaceof SessionResult` – XtremeBaumer Jul 20 '17 at 13:54
  • I am not sure what the question is. The error message seems to be fairly clear: you are checking if a variable of type `SessionResult` is an instance of `NoSession`, which can never be the case, so compiler rejects it. – Dima Jul 20 '17 at 13:55
  • i guess the `NoSession` and `SomeSession` both extend the same super class while `SessionResult` does not. therefore you can only check if each element of `SessionResult` (as long as it is a list) is of type `NoSession`. for more help please tell us what exactly these classes are and what super classes they extend if they do – XtremeBaumer Jul 20 '17 at 14:00
  • Using cast is generally not the better solution – cchantep Jul 20 '17 at 14:08

1 Answers1

5

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$>.

i.e. this will compile

public SessionResult<Nothing$> test() {

    return null;
}

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

while e.g. 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, i.e.:

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.

mikołak
  • 9,605
  • 1
  • 48
  • 70