接下来记录一下scala面向对象的相关知识,包括包、类、抽象类、特质和权限修饰符相关的内容。
scala中,包package的声明比较灵活,可以对比java,如果是java,包的声明必须放在文件最前面。
(1)包结构可以分开写,以下两种方式都可以。
//package clyang.oop.packagex
package clyang
package oop
package packagex
(2)scala文件中可以声明多个包,如果声明位置不在文件的最前面,需要使用大括号。如声明package a,它不是写在文件首行,需要添加{}在后面,这样的包是普通包,里面是不能定义函数的,即使定义了函数编译也不会通过。如果想在package里定义函数,需要使用包对象,参考package object b,它的类型就是package object,可以在里面定义函数,并且可以调用包对象中的函数。
package clyang
package oop
package packagex
//package可以写成上面的格式
/**
* 如果一个包中想定义函数,需设置为包对象
*/
object PackageDemo1 {
def main(args: Array[String]): Unit = {
//对用包对象中的方法
println(b.sum(1,2))
}
}
//1 普通包
package a{
//普通包中不能定义函数,编译报错
//def sum(i:Int,j:Int)=i+j
}
//2 包对象
package object b{
//包对象中才可以定义函数
def sum(i:Int,j:Int)=i+j
}
main方法中调用包对象中sum方法后。
3
(3)包名起名应该尽量简短,符合scala的write less的风格.
//原始包名
package clyang.oop.packagex
//简短的写法
package c.o.p
(4)导包时,如果用到同一个包下的多个类,会将这些类放到{}中,不像java每行一个类。如下,当使用了java.util包下的Date和Scanner,会将它们放到一起,java中的话就是平展开了。
导包时如果导入了同一个包下的多个类,可以使用通配符下划线‘_‘来代替,类似java中的‘*‘。
package clyang.oop.packagex
import java.util.{Date, Scanner} //导入一个包下的多个类,放到{}中
//import java.util._ //通配符下划线的使用
object PackageDemo2 {
def main(args: Array[String]): Unit = {
var Date=new Date()
var scan=new Scanner(System.in)
}
}
(5)当导入不同包下的同名类时,为了代码中更好的区分同名类和具有可读性,可以给类设置别名。
package clyang.oop.packagex
//可以为同名类取别名来区分
import java.util.{Date => utilDate}
import java.sql.{Date => sqlDate}
object PackageDemo2 {
def main(args: Array[String]): Unit = {
//使用时不会引起误解
var utilDate=new utilDate()
var sqlDate=new sqlDate(2019,12,26)
}
}
(6)scala中import语句位置比较灵活,可以放在任意位置,如上面的代码可以修改为如下形式,效果一样。
package clyang.oop.packagex
import java.util.{Date => utilDate}
object PackageDemo2 {
def main(args: Array[String]): Unit = {
var utilDate=new utilDate()
//import语句可以定义在这里
import java.sql.{Date => sqlDate}
var sqlDate=new sqlDate(2019,12,26)
}
}
scala中也有类的定义,类似java,类是是用于创建对象的蓝图或范本,关于scala类的知识点,总结如下。另外为了更好的理解类的信息,使用了反编译,将scala编译后的class文件反编译成java文件,其中反编译软件使用Luyten。
(1)如果一个类中没有任何的属性和方法,则这个类的大括号{}可以省略不写。
(2)类中定义的属性,需要给定一个初始值,不像java直接不给值编译也不报错,但是scala中必须给,当不确定初始值的时候,用下划线‘_‘来代替。
package clyang.oop.classx
object ClassDemo1 {
def main(args: Array[String]): Unit = {
var s=new Star
s.name="messi"
}
}
class Star{
//当属性值不确定时,可以给定下划线代替
var name:String=_
var score:Int=_
private var assist:Int=_
}
//类中没有任何属性和方法,可以省略大括号
class Club
(3)scala中定义的属性,默认是public,scala代码编译后也会变成.class文件,反编译成java文件后,可以看出属性值都会使用private修饰,并对外提供public修饰的get、set方法(name和score属性)。如果属性值使用private修饰,则get、set方法使用private修饰(assist属性),即不对外不提供访问。
以下是Star类反编译后的代码。
package clyang.oop.classx;
import scala.reflect.*;
@ScalaSignature(bytes = "略")
public class Star
{
//一律使用private修饰
private String name;
private int score;
private int assist;
//提供get方法
public String name() {
return this.name;
}
//提供set方法,只是set方法名为name_$eq
public void name_$eq(final String x$1) {
this.name = x$1;
}
public int score() {
return this.score;
}
public void score_$eq(final int x$1) {
this.score = x$1;
}
//private修饰的属性,set、get方法使用private修饰,对外不提供访问
private int assist() {
return this.assist;
}
private void assist_$eq(final int x$1) {
this.assist = x$1;
}
}
以下是ClassDemo1反编译后的代码,可以看出当scala中写成s.name="messi时",实际上是执行了set方法。
package clyang.oop.classx;
public final class ClassDemo1$
{
public static final ClassDemo1$ MODULE$;
static {
new ClassDemo1$();
}
public void main(final String[] args) {
final Star s = new Star();
//调用了name_$eq方法,实际就是set方法
s.name_$eq("messi");
}
private ClassDemo1$() {
MODULE$ = this;
}
}
(4)scala中定义有参构造方法的方式和java中不太一样,可以直接在类后面直接给定参数,这样给定后的参数默认就是类的属性,类中无需再次定义,如name和score属性无需在类中再定义一次了。
package clyang.oop.classx
object ClassDemo2 {
def main(args: Array[String]): Unit = {
//使用构造方法来创建对象
var s=new BigStar("messi",18)
s.printInfo(20)
}
}
//构造方法
protected class BigStar(name:String,score:Int){
//var name:String=_
//var score:Int=_
//这个过滤条件,在反编译后会添加到有参数构造方法中
if (score<0)
throw new IllegalArgumentException
//定义一个函数
def printInfo(assist:Int): Unit ={
//score=30 //提示给一个常量赋值
println(name+" has assists is "+assist)
}
}
//控制台
messi has assists is 20
Process finished with exit code 0
通过反编译后发现,构造方法的参数name和score是final修饰的。如果在类中在定义一个普通函数printInfo,反编译后发现其参数assist也是使用final修饰,在代码中如果想对参数score赋值30会提示不能给一个常量赋值。这样scala中无论是构造函数还是普通函数,里面的参数默认都是常量。
另外对score的过滤条件,反编译后也会进入构造方法。
package clyang.oop.classx;
import scala.reflect.*;
import scala.*;
import scala.collection.mutable.*;
import scala.runtime.*;
@ScalaSignature(bytes = "略")
public class BigStar
{
private final String name;
//普通函数的参数,也使用final修饰
public void printInfo(final int assist) {
Predef$.MODULE$.println((Object)new StringBuilder().append((Object)this.name).append((Object)" has assists is ").append((Object)BoxesRunTime.boxToInteger(assist)).toString());
}
//name和score使用final修饰
public BigStar(final String name, final int score) {
this.name = name;
//过滤条件进入构造方法
if (score < 0) {
throw new IllegalArgumentException();
}
}
}
(5)scala中有主构造器和辅助构造器的概念,主构造器用于创建对象(一般不给参数),辅助构造器用于给对象的属性赋值,并且辅助构造器还能重载。
为了更好的理解,需要借助业务场景,如果定义一个FootballPlayer类,在初始化创建对象的时候,就需要给定能力值(属性,如技巧值,体力值,身高体重等),同时这个对象在随后的岁月中,还可以变化其能力值,就需要能对属性值能进行修改。如果使用上面创建对象的方式,在类后定义属性,就把能力值定死了,怎么改也改不了。这个时候就需要借助主构造器和辅助构造器一起完成,参考如下代码。
其中类后面的括号,代表无参数主构造器,里面的的this(args...)代表辅助构造器,并且辅助构造器中还需要调用主构造器先创建对象,随后才能对对象中的属性值赋值。并且类还提供了changeValue方法,可以对属性值进行修改。通过控制台可以看出实现了业务场景。
package clyang.oop.classx
object ClassDemo3 {
def main(args: Array[String]): Unit = {
var f=new FootballPlayer("clyang",77,80,171)
println(f)
//过了5年
f.changeValue(5)
println(f)
}
}
class FootballPlayer(){//主构造器
//属性
var name:String=_
var skillValue:Int=_
var powerValue:Int=_
var height:Double=_
//辅助构造器
def this(name:String,skillValue:Int,powerValue:Int,height:Double){
//调用主构造器
this()
this.name=name
this.skillValue=skillValue
this.powerValue=powerValue
this.height=height
}
//可以修改属性值
def changeValue(year:Int): Unit ={
if(year<=10){
this.skillValue+=1*year
this.powerValue+=1*year
}else{
this.skillValue-=1*year
this.powerValue-=1*year
}
}
//toString方法
override def toString = s"FootballPlayer($name, $skillValue, $powerValue, $height)"
}
//控制台
FootballPlayer(clyang, 77, 80, 171.0)
FootballPlayer(clyang, 82, 85, 171.0)
Process finished with exit code 0
以下是FootballPlayer反编译后的代码,可以看出主构造器对应无参构造方法,辅助构造器对应有参构造方法,并且有参构造方法中先创建对象,然后再给对象赋值,底层调用了set方法。普通函数changeValue,底层也是调用了set和get方法。
package clyang.oop.classx;
import scala.reflect.*;
import scala.*;
import scala.collection.*;
import scala.runtime.*;
@ScalaSignature(bytes = "略")
public class FootballPlayer
{
private String name;
private int skillValue;
private int powerValue;
private double height;
//get set
public String name() {
return this.name;
}
public void name_$eq(final String x$1) {
this.name = x$1;
}
public int skillValue() {
return this.skillValue;
}
public void skillValue_$eq(final int x$1) {
this.skillValue = x$1;
}
public int powerValue() {
return this.powerValue;
}
public void powerValue_$eq(final int x$1) {
this.powerValue = x$1;
}
public double height() {
return this.height;
}
public void height_$eq(final double x$1) {
this.height = x$1;
}
//普通函数,调用了get set
public void changeValue(final int year) {
if (year <= 10) {
this.skillValue_$eq(this.skillValue() + 1 * year);
this.powerValue_$eq(this.powerValue() + 1 * year);
}
else {
this.skillValue_$eq(this.skillValue() - 1 * year);
this.powerValue_$eq(this.powerValue() - 1 * year);
}
}
@Override
public String toString() {
return new StringContext((Seq)Predef$.MODULE$.wrapRefArray((Object[])new String[] { "FootballPlayer(", ", ", ", ", ", ", ")" })).s((Seq)Predef$.MODULE$.genericWrapArray((Object)new Object[] { this.name(), BoxesRunTime.boxToInteger(this.skillValue()), BoxesRunTime.boxToInteger(this.powerValue()), BoxesRunTime.boxToDouble(this.height()) }));
}
//主构造器对应无参构造方法
public FootballPlayer() {
}
//辅助构造器对应有参构造方法
public FootballPlayer(final String name, final int skillValue, final int powerValue, final double height) {
//先调用无参数构造方法
this();
//set get修改值
this.name_$eq(name);
this.skillValue_$eq(skillValue);
this.powerValue_$eq(powerValue);
this.height_$eq(height);
}
}
(6)辅助构造器可以重载,上面的例子,可以在FootballPlayer类中添加一个属性,忠诚度属性,添加一个辅助构造方法,构成重载。当传入的参数只有一个值时,就匹配新增的辅助构造器,因此控制台打印出"FootballPlayer(null, 0, 0, 0.0, 100)"。
package clyang.oop.classx
object ClassDemo3 {
def main(args: Array[String]): Unit = {
var f=new FootballPlayer("clyang",77,80,171)
println(f)
f.changeValue(5)
println(f)
//辅助构造器使用
var f2=new FootballPlayer(100)
println(f2)
}
}
class FootballPlayer(){
//原属性
var name:String=_
var skillValue:Int=_
var powerValue:Int=_
var height:Double=_
//辅助构造器
def this(name:String,skillValue:Int,powerValue:Int,height:Double){
this()
this.name=name
this.skillValue=skillValue
this.powerValue=powerValue
this.height=height
}
//添加一个属性 忠诚度
var loyalty:Int=_
//辅助构造器可以重载
def this(loyalty:Int){
this()
this.loyalty=loyalty
}
def changeValue(year:Int): Unit ={
if(year<=10){
this.skillValue+=1*year
this.powerValue+=1*year
}else{
this.skillValue-=1*year
this.powerValue-=1*year
}
}
override def toString = s"FootballPlayer($name, $skillValue, $powerValue, $height, $loyalty)"
}
//控制台
FootballPlayer(clyang, 77, 80, 171.0, 0)
FootballPlayer(clyang, 82, 85, 171.0, 0)
FootballPlayer(null, 0, 0, 0.0, 100)
Process finished with exit code 0
(7)一般来说主构造器是不给参数的,也可以给参数,参考如下代码的矩形类。
可以定义一个正方形类来继承矩形类,并且可以重写父类方法,对于java来说没有重写属性的说话,scala中还可以对同名属性进行重写。另外java中支持多继承,但是scala中只支持单继承。
package clyang.oop.classx
object ClassDemo4 {
def main(args: Array[String]): Unit = {
//向上造型,主要手动指定类型
var s:Rectangle=new Square(4)
println(s.getGirth) //16
println(s.name) //正方形
}
}
//矩形类
class Rectangle(x:Int,y:Int){
//方法
def getGirth=2*(x+y)
//属性
val name:String="长方形"
}
//单继承,再定义一个正方形来求面积
class Square(x:Int) extends Rectangle(x,x){
//override关键字,重写父类方法
override def getGirth=4*x
//当存在和父类同名的属性时,也需要使用override关键字
override val name:String="正方形"
}
查看反编译代码,发现除了方法被重写,还重写了属性的get方法(name方法),并且属性在构造方法里被替换。
Rectangle类反编译
package day02.clyang.oop.classx;
import scala.reflect.*;
@ScalaSignature(bytes = "略")
public class Rectangle
{
private final int x;
private final int y;
private final String name;
public int getGirth() {
return 2 * (this.x + this.y);
}
public String name() {
return this.name;
}
public Rectangle(final int x, final int y) {
this.x = x;
this.y = y;
this.name = "\u957f\u65b9\u5f62";//长方形
}
}
Square类反编译
package day02.clyang.oop.classx;
import scala.reflect.*;
@ScalaSignature(bytes = "略")
public class Square extends Rectangle
{
private final int x;
private final String name;
@Override
public int getGirth() {
return 4 * this.x;
}
@Override
public String name() {
return this.name;
}
public Square(final int x) {
super(this.x = x, x);
this.name = "\u6b63\u65b9\u5f62";//正方形
}
}
(8)scala中没有static关键字,如果要实现类似java中静态的效果,可以将方法或者属性定义在object里。如下代码中可以Cal.add(1,2)来直接调用函数,有类似java中工具类调用静态方法的味道。
package clyang.oop.staticy
/**
* object中所有方法和属性均为静态
*/
object StaticDemo1 {
def main(args: Array[String]): Unit = {
//直接调用,有静态的效果
println(Cal.add(1,2))
}
}
object Cal{//可以通过反编译查看到类用final修饰
//可以通过反编译查看到方法用static修饰
def add(i:Int,j:Int)=i+j
}
通过反编译查看发现,object Cal反编译后会形成两个文件,分别是Cal.class和Cal$.class。
Cal.class
这个类可以看出,add方法被static修饰,说明是一个静态方法,静态方法里调用了Cal$...。
package clyang.oop.staticy;
import scala.reflect.*;
@ScalaSignature(bytes = "略")
public final class Cal
{
public static int add(final int i, final int j) {
return Cal$.MODULE$.add(i, j);
}
}
Cal$.class
继续查看Cal$.class的内容,其相比Cal.class更加丰满,发现Module...就是当前对象,上面调用Module...的add方法,说明使用了对象的add方法。虽然调用object的方法,看似是静态方法,其实还是调用对象的方法,这也符合scala面向对象的特点。
package clyang.oop.staticy;
public final class Cal$
{
public static final Cal$ MODULE$;
static {
new Cal$();
}
public int add(final int i, final int j) {
return i + j;
}
private Cal$() {
MODULE$ = this;
}
}
(9)object中只能定义静态的方法和属性,如果一个类既要有静态的方法,又要有非静态的方法,就可以定义同名的object和class,其中静态的定义在object中,非静态的定义在class文件中,两者构成伴生关系,object是class的伴生对象,class是object的伴生类。
package clyang.oop.staticy
/**
* 既需要静态方法又需要非静态方法,可以定义同名的class和object
*/
object StaticDemo2 {
def main(args: Array[String]): Unit = {
val c=new Calc
//非静态调用
c.printTest
//静态调用
Calc.max(1,2)
}
}
//非静态定义在class
class Calc{//object的伴生类
def printTest={
println("我就是最帅")
}
}
//静态定义在object
object Calc{//class的伴生对象
def max(x:Int,y:Int)=if (x>y) x else y
}
查看 Calc类反编译后的代码,发现依然只有两个文件,其中object Calc和class Calc中定义的代码都编译到了一起,合并到Calc.class文件。可以看出object中定义的方法和class中定义的方法都在这个文件中,并且前者用static修饰,依然调用实例对象的max方法,而后者就是普通的方法。
package clyang.oop.staticy;
import scala.reflect.*;
import scala.*;
@ScalaSignature(bytes = "略")
public class Calc
{
//object中定义的方法
public static int max(final int x, final int y) {
return Calc$.MODULE$.max(x, y);
}
//class中定义的方法
public void printTest() {
Predef$.MODULE$.println((Object)"\u6211\u5c31\u662f\u6700\u5e05");//我就是最帅
}
}
scala中也有抽象类,它定义类时,使用abstract关键字即可,在定义方法时,无需再写abstract,只需指定方法名和返回值类型。具体实现类中实现方法的逻辑,使用override关键字。
package clyang.oop.abstractx
object AbstractDemo1 {
def main(args: Array[String]): Unit = {
var c:Shape=new Circle(5)
println(c.getGirth)
println(c.getArea)
}
}
//抽象类
abstract class Shape{
//求周长的抽象方法
def getGirth:Double
//求面积的抽象方法
def getArea:Double
//下面不是抽象方法,已经实现了
def test="hehe I am test"
}
//实现类
class Circle(r:Double) extends Shape{
override def getGirth: Double = 2*3.14*r
override def getArea: Double = 3.14*r*r
}
//控制台
31.400000000000002
78.5
Process finished with exit code 0
scala中没有接口关键字,但是有特质来代替,以下是特质的一些基本知识。
(1)特质定义后,如果想让一个类具有某种特质,需要混入或者继承特质,如果类有父类,如Bicycle,使用with混入特质,如果类没有父类,如Car,则继承特质。
(2)特质可以多混入,如Ferrari类,同时混入Move和Bar特质,有等少特质就使用多少个with。
(3)特质中还可以定义实体方法和属性,如Move特质中定义了speedup方法和feeling属性。
(4)如果在实例化一个对象时想混入特质,也可以,如byd,实现了Move特质,因此可以跑可以加速,但是byd2因为没有实现Move特质,因此不能跑也不能加速。
(5)定义byd混入特质的时候,不能extends继承某个父类,因为不确定BYD类是否有父类,如果可以写将违反单继承的约定。
package clyang.oop.abstractx
object TraitDemo1 {
def main(args: Array[String]): Unit = {
//调用
var b=new Bicycle
b.speed
var c=new Car
c.speed
var f=new Ferrari
f.speed
f.dance
//对象混入特质
var byd=new BYD with Move {//能混入特质,但是不能extends某个父类
override def speed: Unit = {
println("我是国产车,我开的是放心")
}
}
byd.speed
byd.speedup
//没有混入Move特质的对象不能调用speed和speedup方法
var byd2=new BYD
//byd2.speed
//byd2.speedup
}
}
//特质
trait Move{
//方法,没有指定返回值,就是返回Unit
def speed
//特质中可以定义实体方法
def speedup={println("我能加速")}
//特质中可以定义属性
var feeling:String=_
}
class Vehicle
//有父类,使用with混入特质
class Bicycle extends Vehicle with Move {
//实现特质中抽象方法
override def speed: Unit = {
println("我是自行车,我开50迈")
}
}
//没有父类,使用extend继承特质
class Car extends Move{
override def speed: Unit = {
println("我是小轿车,我开150迈")
}
}
//特质
trait Bar{
def dance
}
//特质多混入
class Ferrari extends Vehicle with Move with Bar{
override def speed: Unit = {
println("我是豪车,我开300迈")
}
override def dance: Unit = {
println("我是豪车,我能开着它去酒吧跳舞")
}
}
//对象混入特质
class BYD
控制台打印结果。
我是自行车,我开50迈
我是小轿车,我开150迈
我是豪车,我开300迈
我是豪车,我能开着它去酒吧跳舞
我是国产车,我开的是放心
我能加速
Process finished with exit code 0
scala中权限修饰符主要为public(不写)、protected和private,可以用来修饰类、方法和属性,如果没写权限修饰符,默认就是public,这个的权限范围跟java一样,此外private修饰的,也只能在本类中使用,跟java类似。
权限修饰符 | 本类 | 子类 | 同包类 | 其他类 |
---|---|---|---|---|
默认不写-public | true | true | true | true |
protected | true | true | false | false |
private | true | false | false | false |
(1)public,默认就是public,它修饰的成员在任何地方都可以被访问。
定义了Super类后,里面定义了一个name属性和一个method方法,默认不写任何修饰符,则是public修饰。在本类中可以访问,子类Sub中也可以访问,同包的Same类也可以访问。
package clyang.oop.privilege
/**
* protected、public和private修饰符
*/
//本类
class Super {
var name:String=_
def method(): Unit = {
println("我是方法")
}
class Inner{
name="clyang"
method()
}
}
//子类
class Sub extends Super {
name="clyang"
method()
}
//同包类
class Same {
new Super().method()
new Super().name="clyang"
}
其他类中,也可以访问,所以默认就是任何地方都可以访问。
package clyang.oop.other
import clyang.oop.privilege.Super
/**
* 其他类可以访问public修饰的成员
*/
class Other {
new Super().method()
new Super().name="clyang"
}
(2)protected,和java中的有点区别,scala中使用protected修饰的,只能在本类和子类中使用,同包类中不可用,而java中同包类中是可以用的。依然使用上面的代码,修改属性和方法的权限修饰符,可以看到如下结果。
同包类中不可以使用,对属性和方法的访问都报错‘Symbol xxxx is inaccessible from this place‘。
package clyang.oop.privilege
/**
* protected、public和private修饰符
*/
//本类
class Super {
//protected修饰属性和方法
protected var name:String=_
protected def method(): Unit = {
println("我是方法")
}
class Inner{
name="clyang"
method()
}
}
//子类
class Sub extends Super {
name="clyang"
method()
}
//同包类
class Same {
new Super().method() //报错
new Super().name="clyang" //报错
}
其他类中,跟java一样,是不可以访问的。
package clyang.oop.other
import clyang.oop.privilege.Super
/**
* 其他类不可以访问protected修饰的成员
*/
class Other {
new Super().method() //报错
new Super().name="clyang" //报错
}
(3)private,和java类似,其修饰的成员只能在本类访问,如本类的内部类可以访问。但子类、同包类和其他类均不能访问。
package clyang.oop.privilege
/**
* protected、public和private修饰符
*/
//本类
class Super {
//private修饰属性和方法
private var name:String=_
private def method(): Unit = {
println("我是方法")
}
//内部类中可以使用
class Inner{
name="clyang"
method()
}
}
//子类
class Sub extends Super {
name="clyang" //报错
method() //报错
}
//同包类
class Same {
new Super().method() //报错
new Super().name="clyang" //报错
}
其他类中,跟java一样,是不可以访问的,跟protected的结果一样,代码略。
scala中提供了比java更加细粒度的权限控制,引入了作用域。使用protected[x]或private[x]来修饰成员,可以做到对权限的精确控制,其中x可以是类、包和单例对象。如果不加作用域,以private修饰的成员为例,它只能在本类中访问,加了x后,可以在x范围内被访问到。可以理解为"这个成员除了对[x]中的类或[x]中的包中的类及它们的伴生对像可见外,对其它所有类都是private权限"。
具体可以参考如下代码,其中privilege包下有两个子包,P1和P2,P1包下有两个类Country和Test,还有Test的伴生对象Test,P2包下只有一个类Test,从代码编译提示来看,有如下结果。
(1)protected修饰的方法method,如果不指定作用域P1,在同包下的Test类和Test伴生对象,都不可以使用,均为不可见,指定后均可以访问。同时,因为P2包是P1包的平级包,对method仍然不可以访问,如果P2包是P1包的子孙包,就可以访问到method。
(2)private修饰的类Country,按理是不能在P2包下访问的,当给定作用域为privilege包下,因为P2包在privilege包下,P2包中就可以访问到了。
(3)private修饰的成员rank,因为指定this实例作用域,因此只能通过this.rank访问到,another.rank就访问不到。
(4)private修饰的成员food,由于指定了作用域privilege,在P2包中也能访问到,P2包下test方法中another.food无报错。
(5)private修饰的内部类成员population,原本只能在内部类访问到,当指定作用域为Country类后, new India().population=1200无报错,因为它是在Country类内部进行访问。
package clyang.oop.privilege
/**
* 作用域保护 protected[x]和private[x],x可以是类、包和单例对象
*/
//包1
package P1{
//x是包
private[privilege] class Country{
//x是包
protected[P1] def method(){println("method")}
//x是当前实例
private[this] var rank = 1
//x是包
private[privilege] var food="咖喱"
//测试
def help(another:Country): Unit ={
println(another.method())
println(this.rank)
println(another.rank) //报错
println(another.food)
}
//内部类
class India{
//x是类
private[Country] var population = 1000
//内部类可以用
method()
}
//测试private[Country]
new India().population=1200
}
//测试protected[P1]
class Test{
new Country().method() //不指定P1就报错
}
//测试protected[P1]是否对伴生对象也可见
object Test{
var c=new Country()
c.method() //不指定P1就报错
}
}
//包2
package P2{
import P1._
class Test{
def help(another:Country): Unit ={
println(another.method()) //报错
println(another.food)
}
}
}
想判断添加作用域修饰后成员可不可访问,可以通过判断这个作用域是不是包含访问地所在的范围,以包为例,参考如下代码,可以看出如下结果。
(1)C包下定义的Messi类,当指定作用域B后,只能在C、D、E包下及其子孙包下访问。因为这三个包都在B包下,属于同级包。
(2)C包下的Messi类,只有当指定作用域A包,才能在F包中访问,因为F包和C包有共同的顶级包=>A包。
(3)G包下的Ronald类,因为指定作用域E包,因此H包下能访问,D包由于和E是同级包,访问不到。
package clyang.oop.privilege
/**
* 多层包测试作用域
*/
package A
package B{
package C{
//1 定义类Messi
private[B] class Messi
}
package D{
import clyang.oop.privilege.A.B.C.Messi
//Messi类指定作用域B包,这里能访问到,不指定访问不到
class Test{
new Messi()
//Ronald类指定作用域E包,只有E包下才能访问到
new Ronald()
}
}
package E{
package G{
//2 定义类Ronald
private[E] class Ronald
import clyang.oop.privilege.A.B.C.Messi
//Messi类指定作用域B包,这里能访问到,不指定访问不到
class Test{
new Messi()
}
}
package H{
import clyang.oop.privilege.A.B.E.G.Ronald
//Ronald类指定作用域E包,这里能访问到,不指定访问不到
class Test{
new Ronald()
}
}
}
}
package F{
import clyang.oop.privilege.A.B.C.Messi
//Messi类必须指定作用域A包,才能访问到
class Test{
new Messi()
}
}
上面代码结果以及包的关系简单示意图如下。
以上,是对scala面向对象相关知识的理解总结,可能有不对的地方,后续还需要修正,仅供参考。
参考博文:
(1)https://blog.csdn.net/starkpan/article/details/86633228 伴生类伴生对象
(2)https://www.cnblogs.com/amunote/p/5582303.html
(3)https://www.runoob.com/scala/scala-access-modifiers.html 菜鸟教程
(4)https://www.runoob.com/java/java-inheritance.html 继承
(5)https://blog.csdn.net/smile_from_2015/article/details/80686836 scala编译后的两个class文件的作用
原文:https://www.cnblogs.com/youngchaolin/p/12360647.html