3

我有一个大致如下所示的 Java 接口:

public interface Foo {
  public <T> T bar();
}

我想在 Scala 中实现这个接口,我的所有代码都使用Option. 但是,由于该接口将由 Java 用户使用,我想返回null而不是None. 我尝试了以下方法:

class FooImpl extends Foo {
  def bar[T](): T = {
    val barOpt: Option[T] = getBar()
    barOpt.orNull
  }
}

这会导致以下编译错误:

Expression of type Null does not conform to expected type T

这是有道理的, typeT是不受限制的,它可能是 anInt或其他一些不能是null. 没问题,只要添加T >: Null就完成了,对吧?

class FooImpl extends Foo {
  def bar[T >: Null](): T = {
    val barOpt: Option[T] = getBar()
    barOpt.orNull
  }
}    

不,仍然没有骰子。现在你得到一个新的编译错误:

[error]  method bar has incompatible type

您似乎无法对该接口应用任何限制T并仍然实现该接口。

接下来,我尝试使用asInstanceOf

class FooImpl extends Foo {
  def bar[](): T = {
    val barOpt: Option[T] = getBar()
    barOpt.orNull.asInstanceOf[T]
  }
}    

但这只会带来另一个错误:

Cannot prove that Null <:< T.

有什么办法可以使这项工作?

4

4 回答 4

6

您可以使用 getOrElse 并转换为T

scala> def test[T](t: T): T = { Option(t).getOrElse(null).asInstanceOf[T] }
test: [T](t: T)T

您可以将丑陋的东西移到助手类:

implicit class Defaults[T](val o: Option[T]) extends AnyVal {
  def orDefault(): T = o.getOrElse(null).asInstanceOf[T]
}

def test[T](t: T): T = { Option(t).orDefault }
于 2013-06-20T04:45:28.273 回答
2

仅供参考,如果您使用的是 play,libs 包中已经存在相同的帮助程序:https ://github.com/playframework/Play20/blob/master/framework/src/play/src/main/java/play/库/Scala.java#L12

于 2013-06-22T17:41:40.350 回答
0

以下对我有用:

scala> def test[T >: Null] = null
test: [T >: Null]=> Null

scala> test[String]
res18: Null = null

scala> def test2[T](x : T)(implicit ev: Null <:< T) = x
test2: [T](x: T)(implicit ev: <:<[Null,T])T

scala> test2("Hallo")
res19: java.lang.String = Hallo

scala> test2(null)
res20: Null = null

甚至还有关于 StackOverflow 的讨论,它涵盖了相当不错的主题。有关详细信息,请参见此处

于 2013-06-20T07:04:02.400 回答
0

我怀疑@senia 的答案将被证明是纯 Scala 中可用的最佳选择。如果没有新内容,我会将其标记为“官方”答案。

为了完整起见,这里有一个解决方法,使用 Java 类来封装这个肮脏的业务,给它一个清晰的名称,同样重要的是,记录它存在的原因:

public class JavaOptionHelper {
  // http://stackoverflow.com/questions/17205142/implement-java-interface-in-scala-with-generics-and-return-null
  public static <T> T toValueOrNull(Option<T> option) {
    if (option.isDefined()) {
      return option.get();
    } else {
      return null;
    }
  }
} 

它可以按如下方式使用:

class FooImpl extends Foo {
  def bar[T](): T = {
    val barOpt: Option[T] = getBar()
    JavaOptionHelper.toValueOrNull(barOpt)
  }
}    
于 2013-06-20T05:16:01.973 回答