【问题标题】:Implicit conversions and null隐式转换和 null
【发布时间】:2019-03-04 16:52:22
【问题描述】:

以下代码

import scala.language.implicitConversions

object myObj {
  implicit def nullToInt(x: Null) = 0

  def main(args: Array[String]): Unit = {
    val x = 1 + null
    val y = 1 + nullToInt(null)

    println(x + " " + y)
  }
}

给出以下结果

1null 1

我希望两个 val 都是 Int 并且等于 1。

显然第一个 val 是 String 并且等于“1null”。

Xprint:typer显示源代码被翻译成

package <empty> {
  import scala.language.implicitConversions;
  object myObj extends scala.AnyRef {
    def <init>(): myObj.type = {
      myObj.super.<init>();
      ()
    };
    implicit def nullToInt(x: Null): Int = 0;
    def main(args: Array[String]): Unit = {
      val x: String = 1.+(null);
      val y: Int = 1.+(myObj.this.nullToInt(null));
      scala.Predef.println(x.+(" ").+(y))
    }
  }
}

int 没有接受 null 的符号方法

scala> 10+null
res0: String = 10null

scala> 10*null
<console>:12: error: overloaded method value * with alternatives:
  (x: Double)Double <and>
  (x: Float)Float <and>
  (x: Long)Long <and>
  (x: Int)Int <and>
  (x: Char)Int <and>
  (x: Short)Int <and>
  (x: Byte)Int
 cannot be applied to (Null)
       10*null
         ^

scala> 10-null
<console>:12: error: overloaded method value - with alternatives:
  (x: Double)Double <and>
  (x: Float)Float <and>
  (x: Long)Long <and>
  (x: Int)Int <and>
  (x: Char)Int <and>
  (x: Short)Int <and>
  (x: Byte)Int
 cannot be applied to (Null)
       10-null
         ^

我假设“1”和“null”都被转换为字符串,而不是应用隐式 nullToInt。有人能解释一下编译器是怎么想出来的吗?使用了什么逻辑/工作流程?

另一个问题是是否有办法启用隐式 nullToInt?

附言。我不是在这里谈论最佳实践。随意将问题视为学术兴趣问题。

【问题讨论】:

  • 不是答案,也不是改进问题的建议,而是将+ 的奇怪行为与任何其他方法(此处为+++)进行对比的有趣事实:import language.implicitConversions; class I { def +++(i: I) = "I +++ I" }; class S { def +++(a: Any) = "S +++ Any" }; class N; implicit def n2i(n: N): I = new I; implicit def i2s(i: I): S = new S; println((new I) +++ (new N)); 这转换NI 并调用 I+++,而不是 S 的调用。我不知道为什么它与+相反。

标签: scala operators string-concatenation


【解决方案1】:

我会尝试回答我自己的问题。

这个主题有点误导,实际上根本没有对 val x 的表达式应用任何隐式转换。 NullString 的子类型,而 Int 具有方法 abstract def +(x: String): String,因此它也可以应用于 Null。

Xprint:typer 的输出也证实了这一点,因为它应该显示所有隐式转换,显然它没有显示 x 的任何表达式。

并回答“是否有办法启用隐式 nullToInt”的问题,启用它的唯一方法是在这种情况下显式指定,因为在没有它们的情况下成功编译代码时编译器不会考虑使用任何隐式。

【讨论】:

  • 哦,好吧...好吧,那么它(几乎)是显而易见的。因此,问题 "There are no symbolic methods for int which accept null" 问题的最初假设被证明是不完全正确的。在继续搜索隐式转换之前,我应该仔细检查一下:]
  • 太好了,多亏了这个,我改变了答案,它比我想象的要简单,因为你提到不可能进行转换。我做了一个调试,发现它直接跳到了 StringBuffer 中。我决定看看any2addString 做了什么。请参阅修改后的答案。
【解决方案2】:

所以,@AndreyTyukin 说的是对的,机械地我认为还有更多。至于为什么,有两件事正在发生。

  1. AnyPredef 中使用implicit 进行装饰,请参见以下内容:

    implicit final class any2stringadd[A] extends AnyVal

如您所见,any2stringadd 负责+,您可以在此处查看签名:

def +(other: String): String

更正:没有implicit conversions,比那更简单

  1. 查看Predefany2stringadd 的源代码确实在起作用,如下

implicit final class any2stringadd[A](private val self: A) extends AnyVal { def +(other: String): String = String.valueOf(self) + other }

1String.valueOf 将返回1String。在 Java 中(并使用 jshell 进行验证),添加到 null1 中的 String 将变为 1null

jshell> "1" + null
$1 ==> "1null"

【讨论】:

  • implicitly[Null =&gt; String] 实际上和implicitly[Null &lt;:&lt; String] 是同一个对象,并且这个对象存在是因为,正如我所说,Null 是所有AnyRef 类型的子类型,特别是@ 的子类型987654345@。我有点惊讶有any2stringadd,我以为这只是一个小文档故障,方法实际上是在Any上,但确实有这种转换。
  • 另外,我不明白关于augmentString 等的最后一段 - 它应该演示什么?表达式 new StringOps(null) 在 REPL 中崩溃并出现 NullPointerException。即使有any2stringadd,我仍然不明白为什么它应用于整数1而不是转换参数。
  • 是的,@AndreyTyukin 我忘了提。在 REPL 上它确实失败了,因为我猜它试图为每一行返回一个字符串解释。但是在应用程序或脚本中运行它确实返回nullStringaugmentString 需要用StringOps 包裹java.lang.String,它可能应该被称为RichString。所以我认为正在发生的是augmentString 被调用,然后当它意识到它需要立即unaugmentString 并变成Stringnull 不应该被扩充。
  • 好的,好的:scala.Predef.unaugmentString(new StringOps(null)) 确实在 repl 中返回 String "null" - 这很好。但它仍然没有解释为什么1 + null 不简单地将null 转换为0 并返回1。在我对问题的评论中,+++ 方法正是这样做的:它将N 类型的参数转换为I,然后调用I+++ 以产生I +++ I。因此,right 操作数被隐式转换。在1 + null 的情况下,left 操作数似乎被隐式转换。这是为什么呢?
  • 因为1 + "Foo" 的实际调用是any2stringadd(1) + "Foo",并且由于augment/unaugment 洗钱,null 是一个字符串,它现在是一个称为String"null" .至于它应该返回什么,我不同意,我认为1 + null 应该是一个编译错误,就像它在 Java 中一样,bad operand types for binary operator '+'
猜你喜欢
  • 1970-01-01
  • 2023-03-17
  • 1970-01-01
  • 2013-09-01
  • 1970-01-01
  • 2013-12-22
  • 2015-07-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多