Herencia
## Objetivos de Aprendizaje
- Comprender la herencia de clases
- Dominar clases abstractas
- Trabajar con traits
- Aprender mixins y linearizacion
- Override metodos correctamente
## Herencia de Clases
### Extendiendo una Clase
```scala
class Animal {
def speak(): String = "Some sound"
}
class Dog extends Animal {
override def speak(): String = "Woof!"
}
class Cat extends Animal {
override def speak(): String = "Meow!"
}
val dog: Animal = new Dog()
println(dog.speak()) // Woof!
```
### Modificador Override
El modificador `override` es requerido al sobrescribir metodos concretos:
```scala
class Animal {
def speak(): String = "..."
}
class Dog extends Animal {
override def speak(): String = "Woof!" // override requerido
}
```
### Miembros Final
Previene sobrescritura con `final`:
```scala
class Animal {
final def speak(): String = "..."
}
class Dog extends Animal {
// No se puede sobrescribir speak()
}
```
## Clases Abstractas
### Definiendo Clases Abstractas
```scala
abstract class Shape {
def area(): Double // Abstracto - sin implementacion
def perimeter(): Double // Abstracto
def color(): String = "White" // Concreto - con implementacion
}
```
### Extendiendo Clases Abstractas
```scala
class Circle(val radius: Double) extends Shape {
def area(): Double = Math.PI * radius * radius
def perimeter(): Double = 2 * Math.PI * radius
override def color(): String = "Red"
}
class Rectangle(val width: Double, val height: Double) extends Shape {
def area(): Double = width * height
def perimeter(): Double = 2 * (width + height)
}
```
## Traits
Los traits definen contratos y pueden proporcionar implementaciones por defecto:
```scala
trait Printable {
def print(): Unit = println(toString())
}
trait Serializable {
def serialize(): String
}
```
### Mezclando Traits
```scala
class Document(val title: String) extends Printable with Serializable {
override def toString: String = s"Document($title)"
def serialize(): String = title
}
val doc = new Document("My Doc")
doc.print()
```
### Traits con Metodos Abstractos
```scala
trait Iterator[T] {
def hasNext: Boolean
def next(): T
}
class IntIterator(start: Int, end: Int) extends Iterator[Int] {
private var current = start
def hasNext: Boolean = current < end
def next(): Int = {
val result = current
current += 1
result
}
}
val iter = new IntIterator(1, 5)
while (iter.hasNext) {
println(iter.next())
}
```
## Multiples Traits (Mixins)
### Linearizacion
Cuando una clase extiende multiples traits, Scala usa linearizacion para resolver llamadas a metodos:
```scala
trait A {
def greet(): String = "A"
}
trait B extends A {
override def greet(): String = "B"
}
trait C extends A {
override def greet(): String = "C"
}
class D extends A with B with C {
// Linearizacion: D -> C -> B -> A -> AnyRef -> Any
// greet() resuelve al implementacion de C
}
val d = new D()
d.greet() // "C" (el mas a la derecha gana)
```
### Composicion de Mixins
```scala
trait Logging {
def log(msg: String): Unit = println(s"[LOG] $msg")
}
trait Timestamping extends Logging {
abstract override def log(msg: String): Unit = {
super.log(s"${java.time.Instant.now()}: $msg")
}
}
class Service {
def process(): Unit = log("Processing")
}
object Main extends App {
val service = new Service with Logging
val timestampedService = new Service with Timestamping
service.log("Hello") // [LOG] Hello
timestampedService.log("Hello") // [LOG] 2024-01-01T12:00:00Z: Hello
}
```
## Orden de Constructores
Los constructores se ejecutan de padre a hijo:
```scala
class Animal {
println("Animal constructor")
def speak(): String = "..."
}
class Dog extends Animal {
println("Dog constructor")
override def speak(): String = "Woof!"
}
new Dog()
// Salida:
// Animal constructor
// Dog constructor
```
## Clases Selladas
Las clases selladas restringen quais clases pueden extenderlas:
```scala
sealed abstract class Result
case class Success(value: Int) extends Result
case class Failure(message: String) extends Result
case object Pending extends Result
def describe(result: Result): String = result match {
case Success(v) => s"Success: $v"
case Failure(msg) => s"Failed: $msg"
case Pending => "Pending"
}
// No se pueden agregar nuevas subclases fuera de este archivo
```
## Herencia de Case Class
```scala
// Herencia con case classes
sealed trait Tree
case class Node(left: Tree, right: Tree) extends Tree
case class Leaf(value: Int) extends Tree
def sum(tree: Tree): Int = tree match {
case Node(l, r) => sum(l) + sum(r)
case Leaf(v) => v
}
```
## Seguridad de Objetos
```scala
class Base {
def method(x: Int): String = x.toString
}
class Derived extends Base {
override def method(x: Int): String = s"($x)"
}
val b: Base = new Derived()
b.method(5) // Llama a Derived.method
```
## Rol de AnyRef y Any
```text
Any
├── AnyVal (Int, Double, Boolean, etc.)
└── AnyRef
├── java.lang.Object (en JVM)
├── Clases de usuario
└── scala.Null
```
## Mejores Practicas
1. **Favorecer composicion sobre herencia** cuando sea posible
2. **Usar traits/classes selladas** para emparejamiento de patrones exhaustivo
3. **Usar clases abstractas** cuando necesites parametros de constructor
4. **Usar traits** para reutilizacion de codigo entre clases no relacionadas
5. **Mantener linearizacion simple** - evitar jerarquias de traits profundas
## Resumen
- Las clases extienden otras clases con `extends`
- El modificador `override` es requerido para sobrescribir metodos
- Las clases abstractas pueden tener miembros abstractos y concretos
- Los traits proporcionan interfaz e implementacion por defecto
- La linearizacion determina el orden de resolucion de metodos de traits
- Las clases selladas restringen extension al mismo archivo
- Usa case classes para estructuras de datos inmutables
- Los constructores se ejecutan de padre a hijo
Comments
Comments powered by Giscus
To enable comments, add your Giscus embed code here.
Learn more about Giscus →