【Scala之旅】类与对象
类Scala 中的类是创建对象的模板。它们可以包含统称为成员的方法、值、变量、类型、对象、特征和类。之后将介绍类型、对象和特征。 定义类最小的类定义就是关键字 class User val user = new User 关键字 class Point(var x: Int,var y: Int) { def move(dx: Int,dy: Int): Unit = { x = x + dx y = y + dy } override def toString: String = s"($x,$y)" } val point1 = new Point(2,3) point1.x // 2 println(point1) // prints (x,y) 这个 构造器通过提供默认值,构造函数可以具有可选参数,如下所示: class Point(var x: Int = 0,var y: Int = 0) val origin = new Point // x and y are both set to 0 val point1 = new Point(1) println(point1.x) // prints 1 在这个版本的 class Point(var x: Int = 0,var y: Int = 0) val point2 = new Point(y=2) println(point2.y) // prints 2 这也是一种提高清晰度的好习惯。 私有成员和getter/setter语法默认情况下,成员是公开的。使用 class Point { private var _x = 0 private var _y = 0 private val bound = 100 def x = _x def x_= (newValue: Int): Unit = { if (newValue < bound) _x = newValue else printWarning } def y = _y def y_= (newValue: Int): Unit = { if (newValue < bound) _y = newValue else printWarning } private def printWarning = println("WARNING: Out of bounds") } val point1 = new Point point1.x = 99 point1.y = 101 // prints the warning 在这个版本的 使用 class Point(val x: Int,val y: Int) val point = new Point(1,2) point.x = 3 // <-- does not compile 没有 class Point(x: Int,y: Int) val point = new Point(1,2) point.x // <-- does not compile 混入类“混入”是用来组成类的特性。 abstract class A { val message: String } class B extends A { val message = "I'm an instance of class B" } trait C extends A { def loudMessage = message.toUpperCase() } class D extends B with C val d = new D d.message // I'm an instance of class B d.loudMessage // I'M AN INSTANCE OF CLASS B 类 现在让我们从一个抽象类开始,来看一个更有趣的例子: abstract class AbsIterator { type T def hasNext: Boolean def next(): T } 这个类有一个抽象类型 接下来,我们将实现一个具体类(所有的抽象成员 class StringIterator(s: String) extends AbsIterator { type T = Char private var i = 0 def hasNext = i < s.length def next() = { val ch = s charAt i i += 1 ch } }
现在,让我们创建一个也扩展了 trait RichIterator extends AbsIterator { def foreach(f: T => Unit): Unit = while (hasNext) f(next()) } 只要还有其他元素( 我们希望将 object StringIteratorTest extends App { class RichStringIter extends StringIterator(args(0)) with RichIterator val richStringIter = new RichStringIter richStringIter foreach println } 新的 只有单一继承的话,我们就无法达到这样的灵活性。 嵌套类在 Scala 中,可以让类作将其他类作为自己的成员。与java语言不同,嵌套类是封闭类的成员,在 Scala 中,嵌套类被绑定到外部对象。假设我们希望编译器在编译时阻止我们混合哪些 Node、属于哪些 Graph。路径依赖类型提供了一个解决方案。 为了说明这一差异,我们快速地概述了 Graph 数据类型的实现: class Graph { class Node { var connectedNodes: List[Node] = Nil def connectTo(node: Node) { if (connectedNodes.find(node.equals).isEmpty) { connectedNodes = node :: connectedNodes } } } var nodes: List[Node] = Nil def newNode: Node = { val res = new Node nodes = res :: nodes res } } 这个程序表示一个 Graph 作为 Node 列表( val graph1: Graph = new Graph val node1: graph1.Node = graph1.newNode val node2: graph1.Node = graph1.newNode val node3: graph1.Node = graph1.newNode node1.connectTo(node2) node3.connectTo(node1) 为了清楚起见,我们明确声明了 如果我们现在有两个 Graph,那么 Scala 的类型系统不允许将一个 Graph 中定义的 Node 与另一个 Graph 的 Node 混合,因为另一个 Graph 的 Node 具有不同的类型。 这是一个非法程序: val graph1: Graph = new Graph val node1: graph1.Node = graph1.newNode val node2: graph1.Node = graph1.newNode node1.connectTo(node2) // legal val graph2: Graph = new Graph val node3: graph2.Node = graph2.newNode node1.connectTo(node3) // illegal!
class Graph { class Node { var connectedNodes: List[Graph#Node] = Nil def connectTo(node: Graph#Node) { if (connectedNodes.find(node.equals).isEmpty) { connectedNodes = node :: connectedNodes } } } var nodes: List[Node] = Nil def newNode: Node = { val res = new Node nodes = res :: nodes res } }
注意,这个程序不允许我们将一个 Node 附加到两个不同的 Graph 上。如果我们想要删除这个限制,我们必须将变量 Node 的类型更改为
对象一个对象是一个只有一个实例的类。它被引用时被懒惰地创建,就像懒惰的val一样。 作为顶级的值,一个对象是一个单例。 作为封闭类或本地值的成员,它的行为完全像一个懒惰的val。 定义对象一个对象是一个值。定义一个对象看起来和定义一个类一样,但使用的关键字是 object Box 下面是一个带有方法的对象的例子: package logging object Logger { def info(message: String): Unit = println(s"INFO: $message") } 方法 让我们看看如何在另一个包中使用 import logging.Logger.info class Project(name: String,daysToComplete: Int) class Test { val project1 = new Project("TPS Reports",1) val project2 = new Project("Website redesign",5) info("Created projects") // Prints "INFO: Created projects" } 由于 import 语句, 导入需要++导入符号++的“稳定路径”,并且对象是稳定的路径。 注意:如果一个 伴生对象名称与某个类相同的对象称为伴生对象。相反,该类是对象的伴生类。但伴生类或对象可以访问其伴生的私人成员。在伴生类实例里使用伴生对象的方法和值是没有效果的。 import scala.math._ case class Circle(radius: Double) { import Circle._ def area: Double = calculateArea(radius) } object Circle { private def calculateArea(radius: Double): Double = Pi * pow(radius,2.0) } val circle1 = new Circle(5.0) circle1.area
伴生对象也可以包含工厂方法: class Email(val username: String,val domainName: String) object Email { def fromString(emailString: String): Option[Email] = { emailString.split('@') match { case Array(a,b) => Some(new Email(a,b)) case _ => None } } } val scalaCenterEmail = Email.fromString("scala.center@epfl.ch") scalaCenterEmail match { case Some(email) => println( s"""Registered an email |Username: ${email.username} |Domain name: ${email.domainName} """) case None => println("Error: could not parse email") }
注意:如果类或对象具有伴生,则两者必须在同一个文件中定义。 要在REPL中定义伴生,请将它们定义在同一行上或输入 Java 程序员的注意事项Java 中的 当使用Java代码中的伴生对象时,成员将在具有 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |