【问题标题】:Implement Java interface in Scala, with generics, and return null在 Scala 中使用泛型实现 Java 接口,并返回 null
【发布时间】:2013-06-23 01:41:40
【问题描述】:

我有一个大致如下所示的 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

这是有道理的,类型T 是不受限制的,它可以是Int 或其他不能是null 的类型。没问题,添加T &gt;: 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.

有什么办法可以做到这一点吗?

【问题讨论】:

  • @BevynQ:正如我在描述中所写,我尝试的第一件事是T &gt;: Null,但这导致了第二个错误。
  • 我不知道scala,但它支持这种构造吗? barOpt.&lt;T&gt;orNull?

标签: java scala


【解决方案1】:

您可以使用 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 }

【讨论】:

  • 我知道,但T 可能是AnyVal。您可以隐藏投射查看更新。
【解决方案2】:

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

【讨论】:

    【解决方案3】:

    我怀疑@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)
      }
    }    
    

    【讨论】:

      【解决方案4】:

      以下内容对我有用:

      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 的讨论,它很好地涵盖了这个主题。详情见here

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-10-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-09-28
        相关资源
        最近更新 更多