Scala Java与val的互操作性

时间:2015-04-30 08:03:08

标签: java scala scala-java-interop

在scala中,如果我们有一个班级说,

case class B(b: Int)
object B {
    def main(args: Array[String] {
        val b = B(1)
        b.b = 2 // ** compile time error here **
    }
}
在使用scalac B.scala

进行编译时,

会出错

error: reassignment to val
b.b = 2 
    ^

直到这里一切都很好。

但是,如果我有一个java类A.java

public class A {
     public int a;
     public A(int a) {this.a = a;}
     public void setA(int a_) {this.a = a_;}
     public String toString() {return "A: " + a;}
}

首先用javac A.java编译它, 然后使用val

在scala中使用它
object B {
    def main(args: Array[String] {
        val a = new A(1)
        println("Before assignment: " + a)
        a.a = 2 // ** No error here **
        println("After assignment: " + a)
    }
}

使用B.scala编译scalac B.scala并使用scala B运行。没有错误发生。

它是否会破坏scala中val的意图?

如果我想,在scala中使用java类时,维护实例a的固定值,我该怎么办?

或者实际上有一些文档提到这种行为?

Environment: Scala version 2.11.5 (OpenJDK 64-Bit Server VM, Java 1.7.0_75).
Centos 6.6

修改

谢谢大家的回复。

虽然我很清楚通过让场地最终: public final int a;,可以实现不变性。

我不希望通过修改java代码来解决这个问题。 想象一下,我使用第三方java库,我无法修改他们的代码,但我想确保我的代码不会意外地修改第三方的内部状态,因此来{{{ 1}}。

此外,通过使用val反汇编.class文件,可以清楚地看到scala未通过将javap -c插入类{{1}而使B.b不可变}}。

或者也许有人在解释"魔法"在final后面会有帮助

2 个答案:

答案 0 :(得分:2)

显然,在您的Java类A中,成员a既是公共的又是可变的。要将Java变量视为Scala val,可以在Java类中将其设为final

还要记住,Scala也有var,它与Java变量具有基本相同的行为(final除外)。

编辑1

像这样定义B

case class B(val b: Int, var c: Int)

然后检查已编译的类定义

$ javap B
Compiled from "B.scala"
public class B implements scala.Product,scala.Serializable {
  public static scala.Option<scala.Tuple2<java.lang.Object, java.lang.Object>> unapply(B);
  public static B apply(int, int);
  public static void main(java.lang.String[]);
  public int b();
  public int c();
  public int copy$default$1();
  public int copy$default$2();
  public void c_$eq(int);
  public B copy(int, int);
  public java.lang.String productPrefix();
  public int productArity();
  public java.lang.Object productElement(int);
  public scala.collection.Iterator<java.lang.Object> productIterator();
  public boolean canEqual(java.lang.Object);
  public int hashCode();
  public java.lang.String toString();
  public boolean equals(java.lang.Object);
  public B(int, int);
}

这里要注意的是,对于成员'c',您将拥有public void c_$eq(int);,但对于成员b,没有这样的方法。事实上,在Scala中,赋值结果是一个方法调用。

我希望这更清楚。

答案 1 :(得分:0)

您的疑惑来自val a = new A(1)中的第object B行。这意味着您无法将值重新分配给变量a,但完全可以重新分配a的任何字段。这可以通过将申报标记为最终来限制

尝试在您的java类中创建public int a final,如下所示

public class A {
   public final int a;
   public A(int a) {this.a = a;}
   //public void setA(int a_) {this.a = a_;} //Compilation error here. Cannot assign to a final field
   public String toString() {return "A: " + a;}
}

它将强制无法重新分配类a的字段A