从Java访问元组的奇怪行为 [英] Weird behavior accessing tuple from Java

查看:198
本文介绍了从Java访问元组的奇怪行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在寻找关于我在Java中访问Scala中创建的元组时发现的非常奇怪的行为的解释和/或版本控制细节(如果可能)。

I am looking for explanation and/or versioning details (if possible) about a very strange behavior I found in Java accessing tuples created in Scala.

我将展示我做了一个简单测试的怪异行为。
我创建了这个Scala类:

I will show the weird behavior with an easy test I did. I created this Scala class:

class Foo {
  def intsNullTuple = (null.asInstanceOf[Int], 2)
  def intAndStringNullTuple =  (null.asInstanceOf[Int], "2")
}

然后我运行这个Java程序:

and then I run this Java program:

Tuple2<Object, Object> t = (new Foo()).intsNullTuple();
t._1(); // returns 0 !
t._1; // return null
Tuple2<Object, String> t2 = (new Foo()).intAndStringNullTuple();
t._1(); // returns null
t._1; // return null

是否有人对此有任何解释?而且,在我的测试中,我使用的是Java 1.8和Scala 2.11.8。任何人都可以提供有关使用旧代Scala 2.11和2.10版本以及Java 1.7的Java代码中使用 _1 的兼容性的任何建议吗?我读到无法从Java访问 _1 ,但我可以在测试中访问它。因此,我正在寻找支持它的版本。

does anybody have any explanation on the reason of this? Moreover, in my tests I am using Java 1.8 and Scala 2.11.8. Can anyone provide any suggestion about the compatibility of using _1 from Java code also with older Scala 2.11 and 2.10 versions and Java 1.7? I read that _1 is not accessible from Java, but I can access it in my tests. Thus I am looking for the versions which support it.

谢谢。

推荐答案


有没有人对此有任何解释?

does anybody have any explanation on the reason of this?

这是由于这个原因Scala具有 Tuple2< Int,Int> 重载的特化,而 Tuple2< Int,String> 不T。您可以从 Tuple2 的签名中看到它:

This is due to the fact that Scala has a specialization for the overload of Tuple2<Int, Int>, while Tuple2<Int, String> doesn't. You can see it from the signature of Tuple2:

case class Tuple2[@specialized(Int, Long, Double, Char, Boolean/*, AnyRef*/) +T1, @specialized(Int, Long, Double, Char, Boolean/*, AnyRef*/) +T2](_1: T1, _2: T2)

这意味着Scala编译器为特殊情况发出一个类,其中 T1 T2 是一种专门的元组类型,在我们的例子中有一个特殊的类有两个整数,大致如下:

This means that the Scala compiler emits a class for the special case where T1 and T2 are one of the specialized tuple types, in our example there is a special class taking two ints, roughly like this:

class Tuple2Special(i: Int, j: Int)

我们可以在查看反编译的字节代码时看到这一点:

We can see this when looking at the decompiled byte code:

Compiled from "Foo.scala"
public class com.testing.Foo {
  public scala.Tuple2<java.lang.Object, java.lang.Object> intsNullTuple();
    Code:
       0: new           #12                 // class scala/Tuple2$mcII$sp
       3: dup
       4: aconst_null
       5: invokestatic  #18                 // Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I
       8: iconst_2
       9: invokespecial #22                 // Method scala/Tuple2$mcII$sp."<init>":(II)V
      12: areturn

  public scala.Tuple2<java.lang.Object, java.lang.String> intAndStringNullTuple();
    Code:
       0: new           #27                 // class scala/Tuple2
       3: dup
       4: aconst_null
       5: ldc           #29                 // String 2
       7: invokespecial #32                 // Method scala/Tuple2."<init>":(Ljava/lang/Object;Ljava/lang/Object;)V
      10: areturn

  public com.testing.Foo();
    Code:
       0: aload_0
       1: invokespecial #35                 // Method java/lang/Object."<init>":()V
       4: return
}

如果是 intsNullTuple ,你看到 new 操作码调用 Tuple2 $ mcII $ sp ,这是专门版本。这就是你调用 _1()产生 0 的原因,因为这是值类型<$的默认值c $ c> Int ,而 _1 不是专门的,并且调用重载返回对象,而不是 Int

In the case of intsNullTuple, you see that the new opcode calls Tuple2$mcII$sp, which is the specialized version. That is the reason your call to _1() yields 0, because that's the default value for the value type Int, while _1 isn't specialized and calls the overload returning an Object, not Int.

使用 -Xprint:jvm <编译时, scalac 也可以查看/ code> flag:

This can also be viewed by scalac when compiling with the -Xprint:jvm flag:

λ scalac -Xprint:jvm Foo.scala
[[syntax trees at end of                       jvm]] // Foo.scala
package com.testing {
  class Foo extends Object {
    def intsNullTuple(): Tuple2 = new Tuple2$mcII$sp(scala.Int.unbox(null), 2);
    def intAndStringNullTuple(): Tuple2 = new Tuple2(scala.Int.box(scala.Int.unbox(null)), "2");
    def <init>(): com.testing.Foo = {
      Foo.super.<init>();
      ()
    }
  }
}






另一个有趣的事实是Scala 2.12改变了行为,并使 intAndStringNullTuple print 0 而不是:


Another interesting fact is that Scala 2.12 changed the behavior, and makes intAndStringNullTuple print 0 instead:

public scala.Tuple2<java.lang.Object, java.lang.String> intAndStringNullTuple();
  Code:
     0: new           #27                 // class scala/Tuple2
     3: dup
     4: aconst_null
     5: invokestatic  #18                 // Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I
     8: invokestatic  #31                 // Method scala/runtime/BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer;
     11: ldc           #33                 // String 2
     13: invokespecial #36                 // Method scala/Tuple2."<init>":(Ljava/lang/Object;Ljava/lang/Object;)V
     16: areturn

收益率:

t1 method: 0
t1 field: null
t2 method: 0
t2 field: 0

现在 null 转换为 0 通过 unboxToInt 并通过 boxToInteger Integer 实例中c>。

Since now null gets transformed to 0 via unboxToInt and wrapped inside an Integer instance via boxToInteger.

与Lightbend的相关人员交谈后,这是因为返工在2.12中完成字节码生成器(后端)(参见 https://github.com/scala/ scala / pull / 5176 了解更多)。

After talking to the relevant people at Lightbend, this happened due to the rework done in 2.12 for the bytecode generator (backend) (see https://github.com/scala/scala/pull/5176 for more).

这篇关于从Java访问元组的奇怪行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆