只要我忽略返回值,将函数 [X, Y] X => Y 转换为 X => () 是否安全?

如何解决只要我忽略返回值,将函数 [X, Y] X => Y 转换为 X => () 是否安全?

val f = { x :Int => x }
val g = f.asInstanceOf[Int,Unit] 
g(1) //works

上面的代码有多脆弱?它目前有效,但当然它不是语言规范的一部分。出于这个原因,当然有可能有一天它不会;我的问题是它的可能性有多大,从编译器的合理替代方案的意义上来理解。我不太了解字节码,但我可以通过尝试将 Int 放入 Unit 寄存器来轻松地想象它是否被破坏。另一方面,这种工作实际上可能是记录规范的未记录结果 Scala 编译器和 Java 字节码本身。

我的动机主要在于 @specialized 代码。例如,Iterable 包含以下方法

trait Iterable[E] /* ... */
    def foreach[T](f :E => T) :Unit

选择这个签名可能是为了允许传递 E 的任何函数作为参数。 但在实践中,它几乎总是一个 lambda/方法值。上述方法的问题 是它需要对 ET 进行专门化,从而导致方法变体的平方数,应该避免这种情况。不专门研究 T 是没有意义的,因为 scalac 不会为类型参数的子集发出 @specialized 子类:如果任何 @specialized 参数接收引用(或非专门化)类型作为参数,整个对象将是被擦除超类的一个实例。同样,没有 apply(Int):Object 方法——如果返回类型被擦除,那么被擦除的 apply(Object):Object 将被调用,导致装箱。因此,在这种情况下更可取的是

def foreach(f :E => Unit) :Unit

引入这样的方法(具有不同的名称)并将其用作常规 foreach 的委托目标是可能的,并且不会导致任何装箱,至少只要创建的 lambda 没有有一个引用类型作为它的返回类型。

解决方法

取决于上下文。

大多数时候 Scala 会编译代码,以便返回 Unit 的函数将被称为 JVM 的 void(不返回结果),或者将其视为仅返回 scala.Unit可以丢弃的值。只要 JVM 不尝试对该返回值调用任何内容,它只会假设它是某个 java.lang.Object,如果用于任何东西,则应该检查 scala.Unit,而这永远不会发生。>

但这并不意味着不可能。

val f: Int => Int = _ * 2
val g: Unit => Int = _ => 10
(f.asInstanceOf[Int => Unit] andThen g)(10)
java.lang.ClassCastException: class java.lang.Integer cannot be cast to class scala.runtime.BoxedUnit (java.lang.Integer is in module java.base of loader 'bootstrap'; scala.runtime.BoxedUnit is in unnamed module of loader 'app')
  scala.Function1.$anonfun$andThen$1(Function1.scala:85)
  scala.Function1.apply$mcII$sp(Function1.scala:69)
  ammonite.$sess.cmd27$.<clinit>(cmd27.sc:1)

只要你在自己的代码中调用它,你有完全的控制权,总是知道上下文、测试等应该没问题。

恕我直言,对于世界上基本上每个图书馆或任何需要维护一周以上的代码库来说,这是一个糟糕的假设。

f.andThen(_ => ())

总是更安全,而且我必须有一些非常好的理由来使用 .asInstanceOf 之类的优化,并冒着将来出现一些有趣的错误的风险。

,

asInstanceOf 是一个本质上不安全的通用逃生舱口。当您使用它时,您实际上是在告诉类型检查器盲目信任您。

通常,当您编写 x.asInstanceOf[Y] 时,您是在向编译器承诺值 x 确实属于 Y 类型。

这只有在 x 确实是 Y 类型时才能保证有效。如果不是,编译器将在静态已知 x 是原始类型或 null 时进行一些特殊处理。然后会发生以下两种情况之一:

  • Runtime 会识破你的谎言并抛出一个 ClassCastException
  • 你的谎言和其他可能的事情打破了界限。

在您的示例中,f 属于 Int => Int 类型,这显然不是 Int => Unit 的子类型。换句话说:你在对编译器撒谎,所有保证都是无效的。

这个故事的寓意是:永远不要依赖实现细节来确保正确性,并且应该始终将 asInstanceOf 视为不安全。

正如 Mateusz 所写,f.andThen(_ => ()) 或等效的 { x => {f(x); ()}} 是将某些 A => B 转换为 A => Unit 的唯一正确方法。

您还应该注意,虽然可以生成将专用函数作为参数的专用方法,但 (x => x).asInstanceOf[Int => Unit] 将始终返回未专用的 Function1[Int,Unit],而不是专用类型,因此这无济于事你完全有表现。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?