Как использовать instanceof с объектом scala?

У меня есть следующая иерархия scala:

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]
}

Но я использую этот код из java, и следующий фрагмент кода не компилируется:

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

Почему? А также как я могу использовать instanceof для проверки класса объекта scala?

Ошибка, которую я получаю:

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

person Opal    schedule 20.07.2017    source источник
comment
почему именно не компилируется? я бы предположил, потому что вы уже знаете, что sr instanceof SessionResult<SomeSession>   -  person XtremeBaumer    schedule 20.07.2017
comment
@XtremeBaumer я получаю: неконвертируемые типы; не может преобразовать SessionResult‹SomeSession› в NoSession.   -  person Opal    schedule 20.07.2017
comment
тогда посмотрите, что я сказал дальше в моем предыдущем комментарии. я почти уверен, что это причина   -  person XtremeBaumer    schedule 20.07.2017
comment
@XtremeBaumer, но мне нужно получить конкретный тип.   -  person Opal    schedule 20.07.2017
comment
попробуйте что-то вроде sr.get(0) instanceof NoSession или sr instaceof SessionResult<NoSession>   -  person XtremeBaumer    schedule 20.07.2017
comment
Я не уверен, в чем вопрос. Сообщение об ошибке кажется довольно ясным: вы проверяете, является ли переменная типа SessionResult<SomeSession> экземпляром NoSession, чего никогда не может быть, поэтому компилятор отклоняет его.   -  person Dima    schedule 20.07.2017
comment
Я думаю, что NoSession и SomeSession расширяют один и тот же суперкласс, а SessionResult - нет. поэтому вы можете только проверить, относится ли каждый элемент SessionResult (если это список) к типу NoSession. для получения дополнительной помощи, пожалуйста, сообщите нам, что именно представляют собой эти классы и какие суперклассы они расширяют, если они делают   -  person XtremeBaumer    schedule 20.07.2017
comment
Использование приведения, как правило, не лучшее решение   -  person cchantep    schedule 20.07.2017


Ответы (1)


Проблема заключается в том, что вы жестко ограничиваете общий параметр — NoSession является SessionResult[Nothing].

Таким образом (на языке Java) единственным совместимым вариантом SessionResult<T>, совместимым с SessionResult.NoSession$, может быть SessionResult<Nothing$>.

то есть это будет компилироваться

public SessionResult<Nothing$> test() {

    return null;
}

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

в то время как, например. это не будет

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

    return null;
}

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

К счастью, поскольку NoSession является object, вы можете просто проверить значение singleton по ссылке:

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

(equals требуется, поскольку из-за дисперсии вам нужно преобразовать значение в Object — вы можете сделать это вручную, но equals сэкономит вам некоторое время на этом)


В качестве альтернативы вы можете просто выборочно использовать подстановочный знак для общего параметра, т.е.:

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$);
}

Это очень хакерское решение, но, вероятно, наиболее удобное для кода, который в основном имеет дело с такими проверками на равенство в Java. Как показано в testNay3, вы можете ограничить "сопутствующий ущерб" от использования универсальных типов таким образом, используя простые приведения на месте.

EDIT: изменен на подстановочный знак согласно подсказке Алексея.

person mikołak    schedule 20.07.2017
comment
Я бы предпочел SessionResult<?> необработанному SessionResult. - person Alexey Romanov; 20.07.2017
comment
@AlexeyRomanov: ааа, совсем забыл о подстановочных знаках Java! Спасибо за подсказку! - person mikołak; 21.07.2017