【scala 笔记(7)】 Scala 扩展类、匿名子类、抽象类、样例类、密封类

扩展类

scala 扩展类的方式和Java一样, 使用 extends 关键字,例如:

class Person(val Name:String){
  def show() = {println("person name: " + Name)}
}

class Student(name:String) extends Person(name){

  private var id = 0
  // 重写字段
  override val Name: String = "[%s]".format(name)
  // 辅助构造器
  def this(name:String, id:Int){
    this(name)
    this.id = id
  }

  def learn() = {
    println("learn ... ")
  }
  // 重载方法
  def learn(language:String) = {
    println("learn language " + language)
  }
  // 重写方法
  override def show() = {
    println("student id: %d, name: %s".format(this.id, this.Name))
  }
}

声明Student 对象

scala> val s1 = new Student("borey")
s1: Student = Student@3878be7b

scala> s1.show
student id: 0, name: [borey]

scala> val s2 = new Student("borey"10076)
s2: Student = Student@10667848

scala> s2.learn
learn ... 

scala> s2 learn "scala"
learn language scala

scala> s2.show
student id: 10076, name: [borey]

上述样例包含知识点:

  • 使用extends进行类扩展, 也可以像Java一样把类声明为 final, 这样它就能被扩展;
  • 子类辅助构造器不能直接调用父类构造器; java中可以通用supper(param), scala 不行。
  • 类方法的重载和Java一样;
  • 类方法的重写,必须添加 override; Scala 中 重写一个 非抽象方法 必须使用override修饰符。
  • 类字段的重写,scala的类字段由一个私有字段和取值器/改值器方法构成(scala 类 setter和getter属性)。 你可以用另一个同名的val字段重写一个val(或一个不带参数的def),子类有一个私有字段和一个公有的getter方法, 而这个getter方法重写了超类的getter方法

可以看下 javap 反编译后 上述代码Java的定义:

prod@AWS-TEST-DT:~/borey_zhu/scala$ javap -p Person.class 
Compiled from "Person.scala"
public class Person {
  private final java.lang.String Name;
  public java.lang.String Name();
  public void show();
  public Person(java.lang.String);
}
prod@AWS-TEST-DT:~/borey_zhu/scala$ javap -p Student.class 
Compiled from "Person.scala"
public class Student extends Person {
  private int id;
  private final java.lang.String Name;
  private int id();
  private void id_$eq(int);
  public java.lang.String Name();
  public void learn();
  public void learn(java.lang.String);
  public void show();
  public Student(java.lang.String);
  public Student(java.lang.String, int);
}

匿名子类

和Java一样, 你可以通过包含带有定义或重写的代码块的方式创建一个匿名的子类, 比如:

scala> val w = new Person("Tom"){
     |   def speak(msg:String) {println(Name + " speak:" + msg)}
     | }
w: Person{def speak(msg: String): Unit} = $anon$1@1e54cb33

从技术上讲, 这将会创建出一个 结构类型 的对象。 该类型标记为 Person{def speak(msg: String): Unit}, 你可以用这个类型作为参数类型的定义函数 :

scala> import scala.language.reflectiveCalls
import scala.language.reflectiveCalls

scala> def func(p: Person{def speak(msg: String): Unit}) {
     |   p.speak("hello world")
     | }
func: (p: Person{def speak(msg: String): Unit})Unit

scala> func(w)
Tom speak:hello world

结构类型 : 指的是一组关于抽象方法、字段和类型的规格说明, 这些抽象方法、字段和类型是满足该规则的类型必须具备的。

上述样例表示: 可以对任意只要拥有{def speak(msg: String): Unit}方法的Person子类进行func调用传参。 验证:

scala> class B(name:String) extends Person(name){
     |   def speak(msg:String) {println(msg)}
     | }
defined class B

scala> val b = new B("bo")
b: B = B@6ae3fb94

scala> func(b) // ok 成功
hello world

抽象类

和Java 一样, 你可以用 abstract 关键字来标记不能被实例化的类, 通常这是因为它的某个方法没有被完整定义。 例如:

abstract class Animal(val Name:String){
  val id: Int     // 带有 getter 的抽象字段
  var size:Float  // 带有 getter 和 setter 的抽象字段
  def eat() // 没有方法体 -- 这是一个抽象方法
}

class Dog(name:String) extends Animal(name){
  val id = 1 // 具体的子类必须提供具体的字段, 否则 不带有
  var size = 98.6f
  def eat(){  // 子类重写超类抽象方法时, 你不需要使用 override关键字。
    println(Name + " eat guo tou ...")
  }
}

可以看下 javap 反编译后 上述代码Java的定义:

prod@AWS-TEST-DT:~/borey_zhu/scala$ javap -p Animal.class 
Compiled from "Animal.scala"
public abstract class Animal {
  private final java.lang.String Name;
  public java.lang.String Name();
  public abstract void eat();
  public abstract int id();
  public abstract float size();
  public abstract void size_$eq(float);
  public Animal(java.lang.String);
}
prod@AWS-TEST-DT:~/borey_zhu/scala$ javap -p Dog.class 
Compiled from "Animal.scala"
public class Dog extends Animal {
  private final int id;
  private float size;
  public int id();
  public float size();
  public void size_$eq(float);
  public void eat();
  public Dog(java.lang.String);
}

样例类

样例类是一种特殊的类, 他们经过优化以被用于模式匹配。
例如:

scala> abstract class Animal
defined class Animal

scala> case class Dog(Name:String) extends Animal
defined class Dog

也可以是样例对象

scala> case object Cat extends Animal
defined object Cat

样例类帮我们实现了 apply 和 unapply 方法:

scala> val d = Dog("xiao7")
d: Dog = Dog(xiao7)

scala> d match {
     |   case Dog(name) => println("dog name : " + name)
     |   case _ => ""
     | }
dog name : xiao7
res1: Any = ()

可以看下 javap 反编译看下Scala对样例类的定义:

// prod@AWS-TEST-DT:~/borey_zhu/scala$ javap -p Animal.class 
// Compiled from "Animal.scala"
public abstract class Animal {
  public Animal();
}
// prod@AWS-TEST-DT:~/borey_zhu/scala$ javap -p Dog.class 
// Compiled from "Animal.scala"
public class Dog extends Animal implements scala.Product,scala.Serializable {
  private final java.lang.String Name;
  public static scala.Option<java.lang.String> unapply(Dog);
  public static Dog apply(java.lang.String);
  public static <A> scala.Function1<java.lang.String, A> andThen(scala.Function1<Dog, A>);
  public static <A> scala.Function1<A, Dog> compose(scala.Function1<A, java.lang.String>);
  public java.lang.String Name();
  public Dog copy(java.lang.String);
  public java.lang.String copy$default$1();
  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 Dog(java.lang.String);
}
// prod@AWS-TEST-DT:~/borey_zhu/scala$ javap -p Dog$.class 
// Compiled from "Animal.scala"
public final class Dog$ extends scala.runtime.AbstractFunction1<java.lang.String, Dog> implements scala.Serializable {
  public static Dog$ MODULE$;
  public static {};
  public final java.lang.String toString();
  public Dog apply(java.lang.String);
  public scala.Option<java.lang.String> unapply(Dog);
  private java.lang.Object readResolve();
  public java.lang.Object apply(java.lang.Object);
  private Dog$();
}
// prod@AWS-TEST-DT:~/borey_zhu/scala$ javap -p Cat.class 
// Compiled from "Animal.scala"
public final class Cat {
  public static java.lang.String toString();
  public static int hashCode();
  public static boolean canEqual(java.lang.Object);
  public static scala.collection.Iterator<java.lang.Object> productIterator();
  public static java.lang.Object productElement(int);
  public static int productArity();
  public static java.lang.String productPrefix();
}

// prod@AWS-TEST-DT:~/borey_zhu/scala$ javap -p Cat$.class 
// Compiled from "Animal.scala"
public final class Cat$ extends Animal implements scala.Product,scala.Serializable {
  public static Cat$ MODULE$;
  public static {};
  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();
  private java.lang.Object readResolve();
  private Cat$();
}
// prod@AWS-TEST-DT:~/borey_zhu/scala$ 

当我们申明样例类或对象时, Scala 会帮我们自动实现:

  • 构造器中的每个参数都成为 val (除非它被显式的声明为var ,不建议);
  • 在伴生对象中提供的apply方法;
  • 提供unapply 方法让模式匹配可以工作;
  • 将生成 toString、equals、hashCode 和 copy方法 (除非显式的给出这些方法的定义。 除上述之外, 样例类和其他类完全一样。 可以添加字段和方法,扩展他们等。)

密封类

当你用样例类来做模式匹配时, 你可能想让编译器帮助我们呢确保你已经列出所有可能的选择。 要达到这个目的, 你需要将样例类的通用超类声明为 sealed :

sealed abstract class Color
case object Red extends Color
case object Green extends Color
case object Yellow extends Color

密封类的所有子类都必须在与该密封类相同的文件中定义。那么编译器可以检查模式语句的完整性。让所有同一组样例类都扩展某个密封的类或特质是个好的做法。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值