@MiloXia
2016-04-01T12:37:32.000000Z
字数 7226
阅读 4362
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的话为nullpublic 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>":()V6: returnpublic 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; //标准的valpublic 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);//初始化TestTrait2this.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) => Doubledef 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 + " " + namedef 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, " + namedef 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.toListelements: List[Any] = List(Joe S. Ixpack, 37, None)scala> joe.productElement(1)res4: Any = 37scala> joe.productArity //参数列表个数res6: Int = 3
还有实现了apply和unapply的伴生对象