在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
:
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
后面会有帮助
答案 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
。