@MiloXia
2016-04-01T20:37:32.000000Z
字数 7226
阅读 4133
scala
就是 加 final
所有final成员都是在类初始化(加载)时,或者构造函数里执行的
scala 把其加入在构造函数中 (如果放在类加载,会异常复杂,因为object的实现)
坑:[http://blog.iamzsx.me/show.html?id=674002 ]
class Foo {
val foo = 10
val bar = foo
}class Bar extends Foo {
override val foo = 20
}object Main extends App {
println(new Bar(). bar)
}这段代码输出结果是0,而不是10或20,这是为什么呢?用javap看了一眼编译出来的代码才发现了原因。为了简单起见,我把代码翻译成相应的Java代码:
public class Foo {
private final int foo;
private final int bar;
public int foo() {
return foo ;
}
public int bar() {
return bar ;
}
public Foo() {
foo = 10;
bar = foo();
}
}
public class Bar extends Foo {
private final int foo;
public int foo() {
return foo ;
}
public Bar() {
super();
foo = 20;
}
public static void main(String[] args) {
System.out.println(new Bar().bar());
}
}
关键的地方在L16,这个时候bar的值是通过foo()来获得的,foo()这个时候是Bar.foo(),因此foo()返回的是Bar.foo的值。这个时候,Bar.foo还没有被初始化,所以它的值还是0,bar就被设置为0了。
所有val a = 1 字段都会生成
父类静态成员&静态代码块 -> 子类静态成员&静态代码块 -> 父类成员初始化&父类非静态代码块 -> 父类构造函数 -> 子类成员初始化&子类非静态代码块 -> 子类构造函数
其中静态初始化发生在类加载中 非静态初始化在时
private final int a; //注意private 并且初始值为0 obj的话为null
public int a() { return a}
构造函数中初始化a = 1
因为字段私有,所以所有子类都会考贝一份这样的代码, 又因父类先初始化
这就造成了 val 初始化坑
用lazy 或者改成def 以及早期成员定义(new {val a = ..} with Trait)能解
object XXX {} 会编译出两个类 XXX.class 和 XXX$.class
XXX$.class 为主类
public final class XXX$ extends java.lang.Object implements scala.ScalaObject{
public static final XXX$ MODULE$;
public static {};
Code:
0: new #10; //class Singleton$
3: invokespecial #13; //Method "<init>":()V
6: return
public void method(); //object里的方法
...
}
MODULE$是这个类唯一的实例,这个实例是在static块创建出来的
(类加载时执行) 木有在多加载器的情况下,基本上线程安全
XXX.method 会被 翻译为XXX$.MODULE$.method
这个类里根本没有构造函数
根本就没有机会为这个类创建对象
在Java代码里,如果我们想实现Singleton必须显式声明出一个private的构造函数。而scalac绕过了javac直接生成字节码,它给出了一个用Java语言无法实现的Singleton方案。
XXX.class 就是个代理(XXX.method 会被 翻译为XXX$.MODULE$.method)
public final class XXX extends java.lang.Object{
public static final void method();
Code:
...
}
more [http://www.blogbus.com/dreamhead-logs/58331783.html ]
class XXX 和 object XXX 会被合并(scala2.8之后版本)
object XXX 定义的方法会被放到 XXX.class 以static method形式
static method 和 Singleton一样 会委托MODULE$.method
对加载来说:伴生对象object 加载时会导致class也被加载 (吗??)
more [http://www.blogbus.com/dreamhead-logs/60217908.html ]
trait XXX 会被编译为XXX.class和XXX$class.class
前者是interface,后者为class
trait TestTrait1 {
def foo1() = {println ("foo1")};
}
//编译为
public interface TestTrait1 extends scala.ScalaObject {
public abstract void foo1();
}
//和
public abstract class TestTrait1$class extends java.lang.Object {
public static void foo1(TestTrait1 $this) {
Predef..MODULE$.println("foo1");
}
public static void $init$(TestTrait1 $this){
}
}
一个trait会编译成一个接口和一个实现类,而trait的实现会放在类中当做静态方法
//再看实现类
class X extends TestTrait1
//编译为
public class X implements TestTrait1 {
public void foo1() {
TestTrait1$class.foo1(this);
}
public X(){
TestTrait1$class.$init$(this);
}
}
X在调用静态方法时把this传了进去,抽象类中的静态方法可能会依赖于各个实例不同的状态,所以需要把this传递进去 如
trait TestTrait2 {
val foo = ""
def foo1() = {println ("foo1"+foo)};
}
class X2 extends TestTrait2 {override val foo = "nice."}
//编译为
public abstract interface TestTrait2 {
public abstract void objsets$TestTrait2$_setter_$foo_$eq(String paramString);
public abstract String foo();
public abstract void foo1();
}
public abstract class TestTrait2$class
{
public static void foo1(TestTrait2 $this)
{
Predef..MODULE$.println(new StringBuilder()
.append("foo1")
.append($this.foo()).toString()); //this 在这用
}
public static void $init$(TestTrait2 $this) { //val foo = ""
$this.objsets$TestTrait2$_setter_$foo_$eq("");
}
}
public class X2 implements TestTrait2 {
private final String foo; //标准的val
public void objsets$TestTrait2$_setter_$foo_$eq(String x$1){
}
public void foo1() {
TestTrait2$class.foo1(this);
}
public String foo() { return this.foo; }
public X2(){
TestTrait2$class.$init$(this);//初始化TestTrait2
this.foo = "nice.";
}
}
参考 [http://blog.csdn.net/cuipengfei1/article/details/17465139 ]
Java中想要做到延迟加载,常规的做法是大抵是这样的:
private String str = null;
public String getStr() {
if (str == null) {
str = getStrFromWebService();
}
return str;
}
scala
lazy val str = getStrFromWebService()
其实Scala编译器做的事情和我们手动做的区别不大
private String str;
private volatile boolean bitmap$0;
private String str$lzycompute() {
synchronized (this) { //加锁的
if (!this.bitmap$0) {
this.str = getStrFromWebService();
this.bitmap$0 = true;
}
return this.str;
}
}
public String str() {
return this.bitmap$0 ? this.str : str$lzycompute();
}
双重检测锁(DCL):先通过状态标记变量(做一次位与操作)判断是否已经初始化过
之后版本用byte做bitmap$0,安位标记检查即可,最大8个,大于8个用int,这样减少标记字段数
注: 如果lazy val的初始化依赖外部var变量,此时最好把var 设置为@volatile var
一个类型的type alias,类似于这样的:type t = x。编译器将在所有使用到t的地方把t替换为x。
对于一种操作的type alias,编译器将会根据参数列表和返回值类型的不同将其替换为对应的Function0,Function1,Function2 …… 一直到Function22
type TwoToOne = (String, Int) => Double
def twoToOneImpl: TwoToOne = (str, i) => 1
//变为
public Function2<String, Object, Object> twoToOneImpl(){
return new Hello..anonfun.twoToOneImpl.1(this);
}
局部应用,或者叫做柯里化 (其实有差别)
所谓柯里化就是指把一个接受多个参数的函数的一部分参数写死,剩下的一部分由调用者提供
java只能这么写
public String greet(String greeting, String name) {
return greeting + " " + name;
}
public String sayHello(String name) {
return greet("Hello", name);
}
public String greetXiaoMing(String greeting) {
return greet(greeting, "Xiao Ming");
}
greet用来给某个不确定的人打个不确定的招呼
sayHello用来给某个不确定的人说一句固定的Hello
greetXiaoMing用来给一个固定的人小明打一个不确定的招呼
Scala来表达
def greet(greeting: String, name: String) = greeting + " " + name
def sayHello = greet("hello", _: String)
def greetXiaoMing = greet(_: String, "Xiao Ming")
只是把暂时不确定的参数用下划线指代出来
实现
public String greet(String greeting, String name) {
return new StringBuilder().append(greeting).append(" ").append(name).toString();
}
public Function1<String, String> sayHello() {
return new AbstractFunction1() {
public static final long serialVersionUID = 0L;
public final String apply(String x$1) {
return Hello.this.greet("hello", x$1);
}
};
}
public Function1<String, String> greetXiaoMing() {
return new AbstractFunction1() {
public static final long serialVersionUID = 0L;
public final String apply(String x$2) {
return Hello.this.greet(x$2, "Xiao Ming");
}
};
}
可以看到sayHello和greetXiaoMing并不是返回String的,它们返回的是Function1 of String, String。也就是说直接调用它们俩是得不到我们想要的结果的,必须把这个Function1上的apply再调一下才行
实际上正是如此,这段代码:
sayHello("world")
greetXiaoMing("Ni Hao")
//编译为
sayHello().apply("world");
greetXiaoMing().apply("Ni Hao");
partial application还可以有另一种稍微另类一些的写法 curry
def greet(greeting: String)(name: String) = greeting + " " + name
def sayHello = greet("hello")()
def greetXiaoMing = greet(: String)("Xiao Ming")
def sayHi(name: String) = "Hi, " + name
def sayBye(str: String) = str + ", bye"
val names = List("world", "tom", "xiao ming")
names.map {
name => sayBye(sayHi(name))
}
按照eager evaluation的规则,先运行sayHi,然后把结果传入sayBye,最后得到一个我们想要的结果
我们真正想要的是一个链式操作,一个pipe:把数据用某种操作进行处理,然后把处理后的结果传递给第二个操作继续处理。类似于这样:a.pipe(b),或者是这样:a | b
而Scala的function composition正是做这件事的
names.map(sayHi _ andThen sayBye)
andThen 只是定义在Function1上的一个方法而已
final List names =
List$.MODULE$.apply((Seq)Predef$.MODULE$.wrapRefArray(
(Object[])new String[] { "world", "tom", "xiao ming" }));
return (List)names.map(((Function1)new Serializable() {
public static final long serialVersionUID = 0L;
public final String apply(final String name) {
return Hello.this.sayHi(name);
}
}).andThen((Function1)new Serializable() {
public static final long serialVersionUID = 0L;
public final String apply(final String str) {
return Hello.this.sayBye(str);
}
}), List$.MODULE$.canBuildFrom());
sayHi和sayBye都是被包到了Functoin1里面,调一下第一个Function1的andThen方法,把第二个Function1传进去,会返回一个新的Function1。这个返回的新的Function1就是我们想要的链式操作了。
本质上就是map和flatMap
意义:
不仅在于更短的代码,还在于它提高了信噪比,给我们提供了更加简化的思考模型
case classes mix in scala.Product
scala.Product 提供了productIterator 和 productElement方法 这两个方法不走反射
case class Person(name: String, age: Int, education: Option[String])
val joe = Person(name = "Joe S. Ixpack", age = 37, education = None)
scala> val elements = joe.productIterator.toList
elements: List[Any] = List(Joe S. Ixpack, 37, None)
scala> joe.productElement(1)
res4: Any = 37
scala> joe.productArity //参数列表个数
res6: Int = 3
还有实现了apply和unapply的伴生对象