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.