对象和类 2个月前

编程语言
516
对象和类

使用 class 和类名来创建一个类。类中属性的声明和常量、变量声明一样,唯一的区别就是它们的上下文是类。同样,方法和函数声明也一样。

classShape {
var numberOfSides =0
funcsimpleDescription() ->String {
return"A shape with \(numberOfSides) sides."
    }
}

练习

使用 let 添加一个常量属性,再添加一个接收一个参数的方法。

要创建一个类的实例,在类名后面加上括号。使用点语法来访问实例的属性和方法。

var shape =Shape()
shape.numberOfSides =7
var shapeDescription = shape.simpleDescription()

这个版本的 Shape 类缺少了一些重要的东西:一个构造函数来初始化类实例。使用 init 来创建一个构造器。

classNamedShape {
var numberOfSides: Int=0
var name: String

init(name: String) {
        self.name = name
    }

funcsimpleDescription() ->String {
return"A shape with \(numberOfSides) sides."
    }
}

注意 self 被用来区别实例变量 name 和构造器的参数 name。当你创建实例的时候,像传入函数参数一样给类传入构造器的参数。每个属性都需要赋值——无论是通过声明(就像 numberOfSides)还是通过构造器(就像 name)。

如果你需要在对象释放之前进行一些清理工作,使用 deinit 创建一个析构函数。

子类的定义方法是在它们的类名后面加上父类的名字,用冒号分割。创建类的时候并不需要一个标准的根类,所以你可以根据需要添加或者忽略父类。

子类如果要重写父类的方法的话,需要用 override 标记——如果没有添加 override 就重写父类方法的话编译器会报错。编译器同样会检测 override 标记的方法是否确实在父类中。

classSquare:NamedShape {
var sideLength: Double

init(sideLength: Double, name: String) {
        self.sideLength = sideLength
        super.init(name: name)
        numberOfSides =4
    }

funcarea() ->Double {
return sideLength * sideLength
    }

overridefuncsimpleDescription() ->String {
return"A square with sides of length \(sideLength)."
    }
}
let test =Square(sideLength:5.2, name:"my test square")
test.area()
test.simpleDescription()

练习

创建 NamedShape 的另一个子类 Circle,构造器接收两个参数,一个是半径一个是名称,在子类 Circle 中实现 area()simpleDescription() 方法。

除了简单的存储属性,还有使用 getter 和 setter 的计算属性。

classEquilateralTriangle:NamedShape {
var sideLength: Double=0.0

init(sideLength: Double, name: String) {
        self.sideLength = sideLength
        super.init(name: name)
        numberOfSides =3
    }

var perimeter: Double {
get {
return3.0* sideLength
        }
set {
            sideLength = newValue /3.0
        }
    }

overridefuncsimpleDescription() ->String {
return"An equilateral triangle with sides of length \(sideLength)."
    }
}
var triangle =EquilateralTriangle(sideLength:3.1, name:"a triangle")
print(triangle.perimeter)
triangle.perimeter =9.9
print(triangle.sideLength)

perimeter 的 setter 中,新值的名字是 newValue。你可以在 set 之后的圆括号中显式地设置一个名字。

注意 EquilateralTriangle 类的构造器执行了三步:

  1. 设置子类声明的属性值
  2. 调用父类的构造器
  3. 改变父类定义的属性值。其他的工作比如调用方法、getters 和 setters 也可以在这个阶段完成。

如果你不需要计算属性,但是仍然需要在设置一个新值之前或者之后运行代码,使用 willSetdidSet。写入的代码会在属性值发生改变时调用,但不包含构造器中发生值改变的情况。比如,下面的类确保三角形的边长总是和正方形的边长相同。

classTriangleAndSquare {
var triangle: EquilateralTriangle {
willSet {
            square.sideLength = newValue.sideLength
        }
    }
var square: Square {
willSet {
            triangle.sideLength = newValue.sideLength
        }
    }
init(size: Double, name: String) {
        square =Square(sideLength: size, name: name)
        triangle =EquilateralTriangle(sideLength: size, name: name)
    }
}
var triangleAndSquare =TriangleAndSquare(size:10, name:"another test shape")
print(triangleAndSquare.square.sideLength)
print(triangleAndSquare.triangle.sideLength)
triangleAndSquare.square =Square(sideLength:50, name:"larger square")
print(triangleAndSquare.triangle.sideLength)

处理变量的可选值时,你可以在操作(比如方法、属性和子脚本)之前加 ?。如果 ? 之前的值是 nil? 后面的东西都会被忽略,并且整个表达式返回 nil。否则,可选值会被解包,之后的所有代码都会按照解包后的值运行。在这两种情况下,整个表达式的值也是一个可选值。

let optionalSquare: Square?=Square(sideLength:2.5, name:"optional square")
let sideLength = optionalSquare?.sideLength
image
EchoEcho官方
无论前方如何,请不要后悔与我相遇。
1377
发布数
439
关注者
2223134
累计阅读

热门教程文档

Dart
35小节
Golang
23小节
C#
57小节
Next
43小节
Lua
21小节