一 创建一个简单的类
class Simple{
private var name = "nicky"
def sayHello(): Unit = {
println("Hello,"+this.name)
}
def sayHi = sayHello()
}
object Simple{
def main(args:Array[String]) {
val s = new Simple
s.sayHello()
s.sayHi
}
}
二 字段的get 和 set方法
2.1 变量由private修饰 和 不加修饰符的区别
class Simple {
/*
* JVM会自动定义为private域,但是提供public的getter和setter方法
* 这就意味着该字段可以在其他类或者包中被调用和设置值
*/
var username:String = null
/*
* 定义private 修饰的字段,但是提供private的getter和setter方法
* 这就意味着该字段不能在其他类或者包中被调用和设置值
*/
private var password:String = null;
def sayHello:Unit = {
printf("UserName:"+username+", Password:"+password)
}
}
2.2 定义val 和 var字段的区别
如果我们定义字段成val类型,那么该类只会生成get方法,不会生成set方法,也就是只能取值,不能被赋值,根本原因在于val类型修饰的变量,不可变
class Hello {
val name = "nicky";
def say {
println("Are u "+name+" ?")
println("yes!!")
}
}
object Hello {
def main(args:Array[String]):Unit = {
val hello = new Hello
print(hello.name)//但是可以get
hello.name = "belly"//编译报错,不能set
hello.say
}
}
2.3 private[this]
如果不希望提供get/set方法,那么我们可以通过private[this]来修饰。
private var field 和private[this] var field 比较:
都只是能在本类使用
private var field 可以生成getter和setter方法,但是是私有的
private[this] var field 压根儿就没有get和set方法
class Hello {
private[this] val name = "nicky";
def say {
println("Are u "+name+" ?")
println("yes!!")
}
}
object Hello {
def main(args:Array[String]):Unit = {
val hello = new Hello
print(hello.name)//不能取值
hello.name = "belly"//也不能设置值
hello.say
}
}
2.4 自定义get/set方法
如果希望简单的get/set方法,按照scala提供的语法规则就可以了,根据需要为字段选择合适的修饰符,val var private private[this]
但是你如果希望更好的控制get & set函数,你可以自定义get 和 set函数
首先,scala里面的get和 set方法格式如下:
get函数: 字段名()
set函数: 字段名_=()
其次定义自己的get set方法,不能重写它本身默认的get set方法,也就是说不能提供和属性名一样的get 和 set方法,这应该是一个坑,
如果你需要构造函数参数或者字段提供get set方法建议你在参数前面加一个_下划线,因为scala是不允许get set方法和字段或者构造函数参数名字完全一样
举一个例子:
class Hello{
var message:String= null;
def message = "Message=>" + message;
def message_=(message:String): Unit ={
this.message= message
}
}
这样是有问题的,而且编译会报错
所以你的get set方法应该和属性名不一样:
你可以去一个不同的名字,通常做法是在字段或者构造函数参数前面加一个下划线
class Simple{
private var name:String= null;
private var passwd:String= null;
def sayHello:Unit = {
printf("UserName:"+username+",Password:"+password)
}
def username="UserName => "+this.name
def username_=(name:String){
this.name=name
}
def password=this.passwd
def password_=(passwd:String): Unit ={
this.passwd= passwd
}
}
object ClassClient{
def main (args: Array[String]) {
val s = new Simple
s.username_=("Nicky")
s.password_=("123abcABC")
//UserName:UserName=> Nicky, Password:123abcABC
print(s.sayHello)
}
}
比如Simple里字段是name,我们就可以使用username作为get 和set函数名
2.5让Scala自动生成java风格的getter和setter方法
给field添加@BeanProperty注解即可,注意一旦你使用@BeanProperty,那么该字段不能被private修饰
class Student{
@BeanProperty
var name:String= null;
}
def main(args: Array[String]) {
val stu = new Student
stu.setName("小明")
print(stu.getName)
}
2.6 受保护的字段protected
如果成员是protected,在java中是同包可见,或者不同包但是是其子类也可以见。但是scala中只是子类可见。和private相比,private只要是其他类都不可见,不管你是不是子类,如果private[x],连get 和 set方法都不会有。
区别:
无任何修饰符 | 任何地方都可以使用 |
private[x] | 在定义的类中可以访问,在x包及子包中可以访问 |
private[this] | 只能在定义的类中访问,即使伴生对象也不能访问团 |
private | 在定义的的类及伴生对象中可以访问,其它地方不能访问 |
protected[x] | 在定义的类及子类中可以访问,在x包及子包中可以访问, |
protected[this] | 只能在定义的类及子类中访问,即使伴生对象也不能访问 |
protected | 在定义的类及子类中访问,伴生对象可以访问,其它地方不能访问 |
package com {
package scala {
package traits {
import com.scala.classes.ClassClient
class XNicky {
private var userName:String = "admin";
private[this] var passwd:String = "123abcABC";
private[traits] var ptype:String = "";
protected[Animal] var test:String = "true";
protected var lanuage:String = "Chinese";
protected[this] var country:String = "China";
protected[traits] var city:String = "北京";
private def func1(): Unit ={
println("[private] 方法")
}
private[traits] def func2(): Unit ={
println("[private[traits]] 方法")
}
protected def func3(): Unit ={
println("[protected] 方法")
}
protected[traits] def func4(): Unit ={
println("[protected[traits]] 方法")
}
}
}
}
}
三 构造器Constructor
3.1 主构造器
# Scala中主构造器适合类名放在一起的
# Scala中没有定义在任何方法或者代码块中的代码,属于主构造器的代码,这就意味着在构造该类的对象的时候,就会调用这些代码
# 在创建对象的时候,就必须提供主构造器的相对应的属性,否则报错
# 如果变量类型是val,那么初始化完毕之后,你只能访问该字段,不能进行赋值,原理就在于它只提供get方法,没有提供set方法;
如果是var,提供get和 set方法;如果没有val和 var 不提供get set方法,
# 我们可以在变量类型前面加上private修饰符,可以防止生成get set方法
3.1.1 普通的主构造器
Java中的实现:
public class SKU{
protected StringdisplayName;
protected Stringdesc;
protected float price;
public SKU() {
}
public SKU(StringdisplayName, String desc, float price) {
this.displayName= displayName;
this.desc = desc;
this.price= price;
}
public String getDisplayName() {
return displayName;
}
public String getDesc() {
return desc;
}
public float getPrice() {
return price;
}
public void setDisplayName(StringdisplayName) {
this.displayName= displayName;
}
public void setDesc(Stringdesc) {
this.desc = desc;
}
public void setPrice(floatprice) {
this.price= price;
}
}
Scala实现:
class SKU(vardisplayName:String,var desc:String,varprice:Float) {
print("displayName=>"+ displayName+"\nDesc=>"+desc+"\nPrice=>"+price)
}
3.1.2 为构造参数提供默认值
# 默认参数必须全部提供才能在初始化的时候使用默认构造方法,也就是不必加上参数
# 如果只是一部分提供了默认参数,那么在初始化的时候,不管你有没有提供默认参数,你都需要给该字段赋值
class Product(valname:String="B.C",varkind:String="default",varonsale:Boolean=false) {
println("name:"+name)
println("kind:"+kind)
println("onsale:"+onsale)
}
3.1.3 构造参数没有提供变量的声明
如果构造参数没有提供变量的声明,比如name:String而不是val name:String或者 var name:String,如果在类里面有方法使用到了该字段,该字段就是private[this],就是不能被其他类访问;如果没有使用到,表示没有该字段
class Student(name:String,age:Int) {
println("you name is "+name+", age is"+age);
}
3.2 辅助构造器
在Scala中我们可以定义多个辅助构造器类似于java中构造函数重载,辅助构造函数其实调用的都是主构造函数
class Pizza(varcrustSize:Int, var crustType:String) {
/* 如果你需要构造函数参数或者字段提供get set方法
* 建议你在参数前面加一个_下划线,因为scala是不允许get set方法
* 和字段或者构造函数参数名字完全一样
*/
var _store:String= null;
//0个构造参数。就可以在初始化的时候不用提供参数
def this(){
this(10,"default")
}
//一个辅助的构造参数,就可以在初始化的时候提供一个参数
def this(crustSize:Int){
this(crustSize,"default")
this.store= store
}
def this(store:String) {
this(10,"default")
this._store= store
print("CrustSize=>"+crustSize+"CrustType=>"+crustType+" Store=>"+_store)
}
//没有必要提供2个构造参数的辅助函数,因为那样和主构造器是一样的没有必要,而且还会报错
// def this(crustSize:Int,crustType:String){
// this(crustSize,crustType)
// }
def store= _store
def store_=(store:String): Unit ={
this._store= store
}
}
class Student {
private var name = "";
private var age = 0;
def this(name:String){
this();//主构造器
this.name= name;
}
def this(name:String,age:Int){
this(name);
this.age= age;
}
}
四 将代码块或者函数赋给字段
在Java里面,我们是不可能将代码块或者函数赋给字段的,只能通过get set方法调用函数;Scala中允许直接某一个代码块或者函数直接赋给某一个字段
class Foo{
/*代码块赋给变量*/
val text = {
var lines = "";
try {
lines = io.Source.fromFile("E:\\Bench\\BigData\\scala\\ScalaMain\\content.txt").getLines().mkString
} catch {
case e:Exception=> lines = "ErrorHappened"
}
lines
}
print("text => "+text)
/*函数调用结果赋给字段*/
val content = readContent("E:\\Bench\\BigData\\scala\\ScalaMain\\content.txt")
print("content => "+content)
def readContent(path:String):String = {
var lines = "";
try {
lines = io.Source.fromFile(path).getLines().mkString
} catch {
case e:Exception=> lines = "ErrorHappened"
}
lines
}
}
五 设置未初始化的var字段类型
在一个类中,设置一个未初始化的var字段类型,可以这样开始:
var x = 然后思考如何表达?
解决办法:通常会把字段定义为一个Option。对于某些类型,如string和数字类型字段,你们可以给他指定默认的初始值
case class Person(varusername:String,varpassword:String) {
var age = 0;
var firstName = "";
var lastName = "";
}
但这时候你可能需要一个Address对象来存储或者表达用户的地址信息,你可以这样赋一个None:Option[Address]
case class Person(varusername:String,varpassword:String) {
var age = 0;
var firstName = "";
var lastName = "";
var address = None:Option[Address]
}
case class Address(city:String,state:String,zip:String)
然后当用户提供一个地址的时候,可以赋给一个字段Some[Address]的值,像这样:
object ClassClient{
def main (args: Array[String]) {
val p = new Person("nicky","123abcABC")
p.address= Some(Address("Sanfrancisco","CA","02134"))
/**
* 需要访问这个address字段的时候有许多不同的方法可以使用,如果想要打印一个Address字段,
* 可以在 address上调用foreach方法
*
* 如果该字段没有值,address就是一个None,调用foreach方法是没有任何害处的,循环跳过
* 如果该字段有值,他会是一个Some[Address],就会循环打印出来
*/
p.address.foreach(a=>
print(a.city,a.state,a.zip)
)
}
}
六 在继承类的时候,处理构造函数参数
将父类的构造函数参数定义为val 或者 var。当定义一个子类构造函数时,不要用var或者val声明和父类的公用字段。然后在子类中用val 或者 var字段定义新的构造函数参数
简单一句话: 子类和父类共同字段,父类可以使用var val,但是子类不能加这些修饰符,子类自己独立的字段可以使用var 或者 val变量修饰符
先定义一个父类Parent:
class Parent(varname:String,var address:Address) {
override def toString:String = if (address == null)name else s"$name @$address"
}
再定一个子类Children
class Children(name:String,address:Address,varage:Int) extends Parent(name,address){
}
测试:
object ClassClient{
def main (args: Array[String]) {
val p = new Parent("Nicky",newAddress("红瓦寺","成都","637300"))
println(p.name,p.address.city)
val c = new Children("Bely",newAddress("磨子桥","成都","637301"),28)
println(c.name,c.address.city,c.age)
}
}
为什么要这样做?
首先我们需要知道的是:父类已经提供val 或者var 变量修饰符针对共有字段,那么势必就会生成默认的get 和 set方法,比如name字段,则会生成name() 和 name_=,如果子类也是用该字段,但是仍然提供val 或者 var就有问题了,我们知道这会和父类的相同的字段的get set方法冲突,因为父类已经有了嘛。所以 子类只能不加 val 或者 var 修饰符,这样scala就不会生成get set方法。
其次:如果父类在val 和 var前面加了修饰符private,那么子类可以加val 或者 var吗?
根据上面的解释,我们知道是可以,因为在父类的构造函数参数前面的变量修饰符前加了private,不管设计var 还是 val都不会生成get 和 set方法,也就说父类没有,父类没有话,子类当然可以有了,所以子类可以再加上val 或者 var
七 调用父类的构造函数
在子类创建构造函数的时候,需要控制被调用的父类构造函数
可以在子类的主构造函数中控制被调用的父类的构造函数,但是却无法控制被子类辅助构造调用的父类构造函数
class Animal(varname:String) {
print(name)
}
class Dog(name:String)extends Animal(name){
}
如果父类有多个构造函数,Dog可以调用其中任何一个,比如下面Dog类的主构造函数通过extends的时候语句指定构造函数调用Animal类中接收参数的辅助构造函数
class Animal(varname:String,var age:Int) {
def this(name:String){
this(name,0)
}
}
然后子类在继承的时候,既可以指定调用父类的一个参数构造函数,也可以指定两个参数的构造函数
class Dog(name:String)extends Animal(name){
}
class Cat(name:String)extends Animal(name,5){
}
八 何时使用抽象类
第一种情况:需要创建一个有构造函数的父类
第二种情况:需要被Java调用
在抽象父类定义属性:
一个抽象类定义抽象或者实现的属性,这样就可以被子类锁引用
abstract class Pet(name:String,age:Int) {
/*定义抽象属性,子类来实现*/
val greeting:String
var color:String
def sayHello{println(greeting)}
/*定义抽象方法,子类来实现*/
def action
override def toString:String= s"I say: $greeting, and I'm$age, my color is$color"
}
class Dog(name:String,age:Int)extends Pet(name,age){
val greeting = "汪汪"
var color = "白色"
override def action: Unit = {
print("我正在啃骨头")
}
}
class Cat(name:String,age:Int) extendsPet(name,age){
val greeting = "喵喵"
var color = "黑色"
override def action: Unit = {
print("我正在卖萌")
}
}
object ClassClient{
def main (args: Array[String]): Unit = {
val dog = new Dog("旺财",5)
val cat = new Cat("喵咪",2)
dog.sayHello
cat.sayHello
dog.action
cat.action
}
}
注意:抽象父类属性使用的变量修饰符val和var应该类型一致
九 用case类
当我们使用case类的时候,它会在底层为我们生成一些代码,这样为我们开发提供了一些好处:
# 会生成apply方法,所以我们在初始化对象的时候,就可以不用new
# 默认构造函数参数类型是val,会自动生成get方法,如果是var会自动生成var
# 生成一个默认的tostring方法
# 会产生一个equals方法和hashcode方法
# 回产生一个copy方法
case class People(name:String,var age:Int) {
def func(): Unit ={
println(name,age)
}
}
object ClassClient{
def main (args: Array[String]): Unit = {
val p = People("Bala",25)
p.func()
println(p.toString)
println(p.hashCode())
val p1 = p.copy()
print(p1.func())
}
}
十 创建内部类
创建一个内部类,这个类不会被外部API调用
10.1Java和Scala也有不一样的地方
import java.util.ArrayList;
import java.util.List;
public class Classes {
class Student {
private Stringname;
public Student(Stringname) {
super();
this.name =name;
}
}
List<Student> students =newArrayList<Student>();
public Student getStudent(Stringname){
return new Student(name);
}
public static void main(String[] args) {
Classes c1 = new Classes();
Student s1 = c1.getStudent("Nicky");
c1.students.add(s1);
Classes c2 = new Classes();
Student s2 = c2.getStudent("Bell");
c1.students.add(s2);
}
}
Classes实例 c1的students是可以添加Classes 实例产生的Student实例
class Classes(name:String) {
class Student(val name:String) {}
val students = new ArrayBuffer[Student]()
def getStudent(name:String): Student = {
new Student(name)
}
}
object ClassClient {
def main (args: Array[String]): Unit = {
val c1 = new Classes("Java")
val c2 = new Classes("C#")
val s1 = c1.getStudent("LiLei")
val s2 = c1.getStudent("HanMeimei")
val s3 = c2.getStudent("Jim")
val s4 = c2.getStudent("Kitty")
c1.students += s1
c1.students += s1
c2.students += s3
c2.students += s4
c1.students += s3 //不能添加,类型不匹配
}
}
在Java里只要内部类是同一个类型,都可以加入到某一个班级中,而不用区别Student是哪一个Classes产生的实例
Scala里,即使内部类是同一个类型,也要区别Student属于的Class实例不一样
10.2内部类访问外部类
方式一:外部类名.this.成员
class Classes(val name:String ="全真教") {
var age:Int =0;
def this(age:Int){
this();
this.age = age;
}
class Student(val name: String) {
def info():Unit = {
Classes.this.print();
println("OuterClass Name: "+Classes.this.name+"; InnerClass Name: "+name)
}
}
def print(){
println(this.name+" "+this.age)
}
}
方式二 :别名,outrer =>
但是必须放在第一行
class Classes(val name:String ="全真教") {
outer =>
class Student(val name: String) {
def info():Unit = {
outer.print();
println("OuterClass Name: "+outer.name+"; Inner Class Name: "+name)
}
}
var age:Int =0;
def this(age:Int){
this();
this.age = age;
}
def print(){
println(this.name+" "+this.age)
}
}