scala类
git地址
https://github.com/a18792721831/studyScala.git
1.scala类
scala类和java类定义类似,格式、组成相同。
不同点是scala默认是public,而java默认是package(包内)
首先创建一个scala的类
class MyTest {
var id: Long = _
var name: String = _
private var age: Int = _
def getAge: Int = age
def setAge(age: Int): Unit = this.age = age
override def toString: String = "id:" + this.id +",name:" + this.name + ",age:" + this.age
val gAge = () => println(this.age)
val sAge = (age: Int) => {
this.age = age
age
}
}
这个类很简单:
两个共有属性,一个私有属性,并且属性可变,有默认值。
为私有属性提供了读取、设置方法。
重写了toString方法。
还有两个函数,分别是打印函数和设置函数。
然后创建一个伴生对象
object MyTest{
def main(args: Array[String]): Unit = {
val myTest = new MyTest()
println(myTest)
myTest.id = 1
myTest.name = "lili"
myTest.age = 234
println(myTest)
println(myTest.getAge)
myTest.setAge(0)
println(myTest)
println()
println(myTest.gAge)
println(myTest.sAge)
myTest.gAge
println(myTest.sAge(3))
println(myTest.getAge)
}
}
先别问为什么需要创建伴生对象,后面会研究到的。
先执行看看结果
id:0,name:null,age:0
id:1,name:lili,age:234
234
id:1,name:lili,age:0
com.study.MyTest$$Lambda$1/2093631819@1ff8b8f
com.study.MyTest$$Lambda$2/1828972342@387c703b
3
3
Process finished with exit code 0
可以看到,scala类的属性、方法和java基本一样。
scala的函数与java的lambda是一个。
接下来我们看下反编译java
有点意思,好像与我们的class和object能对应上。
这两个文件,用class查看软件只能看伴生对象的class文件
接下来用在线反编译等手段,看下scala类
import java.lang.invoke.SerializedLambda;
import scala.runtime.BoxesRunTime;
import scala.Predef$;
import scala.Function1;
import scala.runtime.BoxedUnit;
import scala.Function0;
import scala.reflect.ScalaSignature;
public class MyTest
{
private long id;
private String name;
private int com$study$MyTest$$age;
private final Function0<BoxedUnit> gAge;
private final Function1<Object, Object> sAge;
public static void main(final String[] args) {
MyTest$.MODULE$.main(args);
}
public long id() {
return this.id;
}
public void id_$eq(final long x$1) {
this.id = x$1;
}
public String name() {
return this.name;
}
public void name_$eq(final String x$1) {
this.name = x$1;
}
private int com$study$MyTest$$age() {
return this.com$study$MyTest$$age;
}
public void com$study$MyTest$$age_$eq(final int x$1) {
this.com$study$MyTest$$age = x$1;
}
public int getAge() {
return this.com$study$MyTest$$age();
}
public void setAge(final int age) {
this.com$study$MyTest$$age_$eq(age);
}
@Override
public String toString() {
return new StringBuilder(14).append("id:").append(this.id()).append(",name:").append(this.name()).append(",age:").append(this.com$study$MyTest$$age()).toString();
}
public Function0<BoxedUnit> gAge() {
return this.gAge;
}
public Function1<Object, Object> sAge() {
return this.sAge;
}
public MyTest() {
this.gAge = (Function0<BoxedUnit>)(() -> Predef$.MODULE$.println((Object)BoxesRunTime.boxToInteger($this.com$study$MyTest$$age())));
this.sAge = (Function1<Object, Object>)(age -> {
$this.com$study$MyTest$$age_$eq(age);
return age;
});
}
}
可以看到,属性都被编译为了私有,同时生成了一些用到的方法
根据名字猜测:
这个main就是java中我们经常写的main
被代理到了内部类中了。内部类就是我们的伴生对象。
应该是提供直接访问的。
在Java中,我们的属性如果是公有或者保护或者默认,那么在同路径的类中是可以直接使用的。
在scala中默认是公有的。
所以可以直接用。
但是,实际上还是私有的,只不过生成了属性同名的访问方法。造成了我们对于私有属性可以直接使用的假像(对于无参方法,会自动填充小括弧)
应该是为了实现公有属性直接赋值。
toString方法用StringBuilder进行了优化。
函数是在构造时进行初始化的。
综上,原本期望使用_
进行初始化属性,结果反编译没有体现出来。。。
1.1 scala类的方法调用
之前就知道,在scala中,如果没有参数,可以将方法的小括弧省略。
但是,还有一句,如果在定义时,方法就没有小括弧,那么在使用时,也不能加小括弧。
比如:
class MyApply {
def apply() = println("apply class with ()")
def apply1 = println("apply1 class without ()")
def apply2() = println("apply2 class without ()")
}
我们创建了一个scala类,scala类有三个方法
apply()
apply1
apply2()
然后我们创建一个object去调用
object MyTestApply {
def main(args: Array[String]): Unit = {
val myApply = new MyApply
println(myApply)
myApply()
myApply.apply1
myApply.apply2
myApply.apply2()
}
}
object也非常的简单,首先创建了一个不可变对象,然后打印了对象,然后分别调用这三个方法。
输出如下:
com.study.MyApply@2be94b0f
apply class with ()
apply1 class without ()
apply2 class without ()
apply2 class without ()
Process finished with exit code 0
接下来看看Java代码
这是scala类的
这是object对应的类的
这是object对应的内部类
1.2 scala类的apply方法
在调用scala类的apply方法时,可以不显示的调用,而是使用变量名加括弧调用。
注意,这样方式只适用于有小括弧的apply方法。
2. scala Object 单例对象
单例对象用于持有一个类的唯一实例。
为什么?
看看1中的例子。
我们创建了一个scala类,一个单例对象。
反编译结果是两个类,一个内部类。
首先scala类对应了反编译Java类,而单例对象生成了反编译Java类和内部类。
单例对象的反编译Java类作为执行入口,将实际的操作转到内部类的main方法
在内部类中定义了静态公有不可继承的MODULE$对象。反编译Java类通过这个对象调用实际的内部类的main方法。
所以,可以这么认为,因为内部类中的MODULE$对象是实际操作入口,也是静态的,直接new的。所以object内部类持有了object的唯一入口,也即唯一实例。
MD,有点绕。后面慢慢理解吧。
反正就这样用。
2.1 单例对象的apply
在单例对象中也可以实现scala类中apply方法,直接让变量+()调用apply方法。
在单例对象中也可以实现。不过,因为单例对象是单例的,所以可以直接Object名字+()调用单例对象的apply
举例如下:
首先创建一个object:
object TestApply {
def apply() = println("TestApply")
}
调用
object TestMain{
def main(args: Array[String]): Unit = {
TestApply()
}
}
运行结果
TestApply
Process finished with exit code 0
看下反编译结果
3. Trait 特质
怎么说呢,这玩意和Java接口应该是一个东西(jdk1.8之后的接口,可以写默认方法)
创建一个特质
class实现
class继承
object实现
内部匿名实现
测试
所以说,Java中怎么用接口,在scala中就怎么用trait
不过,scala貌似不支持上下转型。
3.1 实现多个特质
我们在上面的基础上在创建一个特质
然后创建scala类实现
创建object调用
执行结果
这玩意和接口完全相同。
看下反编译代码
但是多多少少还是和没有实现特质的scala类有一定的区别。
没有实现特质的scala类的反编译代码实际所有的操作都在内部类(应该不叫内部类…不过都是用$进行区分,一个在钱,一个在后),但是实现了特质的scala类的实现都在外部类
3.2 new对象时实现特质
scala比较骚的一点是可以在new对象时实现特质。
Java貌似不行。
这就使得scala非常的灵活。
举个例子:
首先创建一个普通的类
在new对象时,实现特质
看看反编译代码
这个应该是继承原来的scala类,实现了特质的一个内部类
表现为new后面实现了方法(内部类)
还有就是类名前面有$标识等等。
3.3 函数实现特质
在scala中,主要是对象的函数式编程。
在某些资料中,函数是这样被定义的:
函数是一些特质实现的集合。
所以函数的本质是类的实例。
举个例子
首先看看函数的特质
接下来实现它
然后进行使用
输出
当然,还有快捷的写法
使用 =>
语法糖
看下反编译代码
3.4 传名参数
传名参数是指类似如下的定义
表示方法test的参数是一个函数,=>语法糖,而函数的执行结果是String,然后test对函数的执行结果的String进行操作。
举例:
这里存在将方法自动展开,即EAT展开。
如果在需要函数的地方传入了方法,自动将方法转为函数。