CSE116的学习笔记 Lec2-1:Inheritance
Inheritance
Scala Type Hierarchy
- All objects share Any as their base types
- Classes extending AnyVal will be stored on the stack
- Classes extending AnyRef will be stored on the heap
Overview
Let’s do some world building
If we’re making a game, we’ll want various objects that will interact with each other
we’ll setup a simple game where
- Each player has a set health and strength
- Players can pick up and throw balls
- If a player gets hit with a ball, they lose health
- Players can collect health potions to regain health
Note: We might not build this full game, but we will build some of the game mechanics.
Objects Review
- We’ll need different objects for this game
- Player
- Ball
- HealthPotion
1 | object Player { |
1 | object ball { |
1 | object HealthPotion { |
- But this is restrictive
- Game can only have one Ball, one HealthPotion and on Player
- Can play, but mot very fun
Classes Review
- This is why we use classes
- Classes let use create multiple objects of type Ball, HealthPotion, and Player.
1 | class Player(var location: PhysicsVector, |
1 | class Ball(var location: PhysicsVector, |
1 | class HealthPotion(var location: PhysicsVector, |
- Use the class to create multiple objects with different states
1 | var ball1: Ball = new Ball( |
1 | var ball2: Ball = new Ball( |
Inheritance
- Use inheritance to create classes with different behavior
- Observe: Ball and HealthPotion have a lot in common
- Can add much more common functionality (that doesn’t fit on a slide)
- Compute mass of a potion based on volume
- Compute momentum of both types based on mass * velocity
- Method defining behavior when either hits the ground(bounce or shatter)
Factor out common state and behavior into a new calss
Ball and HealthPotion calsses inherent the state and behavior of InanimateObject
Ball and HealthPotion add their specific state and behavior
New class defines what every inheriting class must define
Any behavior that is to be defined by inheriting classes is declared abstract
- We call this an abstract class
- Cannot create objects of abstract
Inheriting classes will define all abstract behavior
- We call these concrete classes
1
2
3
4
5
6abstract class InanimateObject(var location: PhysicsVector, var dimension: PhysicsVector, var velocity: PhysicsVector){
def objectMass(): Double
def use(player: Player): Unit
}
- We call these concrete classes
Use the extends keyword to inherent another class
- Extend the definition of InanimateObject
- We call IanimateObject the superclass of Ball
1
2
3
4
5
6abstract class InanimateObject(var location: PhysicsVector, var dimension: PhysicsVector, var velocity: PhysicsVector){
def objectMass(): Double
def use(player: Player): Unit
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17class Ball(location: PhysicsVector,
dimensions: PhysicsVector,
velocity: PhysicsVector,
mass: Double)
extends InanimateObject(location, dimensions, velocity){
override def objectMass(): Double = {
this.mass
}
override def use(player: Player): Unit = {
this.velocity.x = player.orientation.x * player.strength
this.velocity.y = player.orientation.y * player.strength
this.velocity.z = player.strength
}
}
Ball has it’s own constructor
Ball must call InanimateObject’s constructor
var / val declared in concrete class to make these public
Implement all abstract behavior
Use the override keyword when overwritting behavior from the superclass
Override all abstract methods with behavior for this class
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23abstract class InanimateObject(var location: PhysicsVector, var dimension: PhysicsVector, var velocity: PhysicsVector){
def objectMass(): Double
def use(player: Player): Unit
}
```
```scala
class HealthPotion(location: PhysicsVector,
dimensions: PhysicsVector,
velocity: PhysicsVector,
val volume: Int)
extend InanimateObject(location, dimensions, velocity) {
override def objectMass(): Double = {
val massPerVolume: Double = 7.0
volume * massPerVolume
}
override def use(player: Player): Unit = {
player.health = (player.health + this.volume).min(player.macHealth)
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17class Ball(location: PhysicsVector,
dimensions: PhysicsVector,
velocity: PhysicsVector,
mass: Double)
extend InanimateObject(location, dimensions, velocity){
override def objectMass(): Double = {
this.mass
}
override def use(player: Player): Unit = {
this.velocity.x = player.orientation.x * player.strength
this.velocity.y = player.orientation.y * player.strength
this.velocity.z = player.strength
}
}Define different behavior for each base class
Define similar types with some difference
1 | abstract class InanimateObject(var location: PhysicsVector, var dimensions: PhysicsVector, var velocity: PhysicsVector) { |
OK, BUT Y THO?
Add behavior to InanimateObject
Behavior is added to ALL inheriting classes
We may want many, many more subtypes of InanimateObjects in our game
Any common functionality added to InanimateObject
- Easy to add functionality to ALL subtypes will very little effort
But wait!
- There’s more
1 | abstract class InanimateObject(var location: PhysicsVector, var dimensions: PhysicsVector, var inputVelocity: PhysicsVector) extends DynamicObject(location, dimensions){ |
If we want Ball, HealthPotion, and all other InanimateObjects to work with our physics engine
- Extend DynamicObject!
Note that the velocity is inherited
The velocity parameter in the constructor must have a different name that the inherited variable
- Allows us to assign its value to the state variable
- They would both be referred to with this causing a name conflict
No name conflict with multiple location / dimension since they are only in the header
Scala Type Hierarchy
- All objects share Any as their base types
- Classes extending AnyVal will be stored on the stack
- Classes extending AnyRef will be stored on the heap
- Classes you define extend AnyRef by default
- HealthPotion has 6 different types
1
2
3
4
5
6val potion1: HealthPotion = new HealthPotion(new PhysicsVector(), new PhysicsVector(), new PhysicsVector(), 6)
val potion2: InanimateObject = new HealthPotion(new PhysicsVector(), new PhysicsVector(), new PhysicsVector(), 6)
val potion3: DynamicObject = new HealthPotion(new PhysicsVector(), new PhysicsVector(), new PhysicsVector(), 6)
val potion4: GameObject = new HealthPotion(new PhysicsVector(), new PhysicsVector(), new PhysicsVector(), 6)
val potion5: AnyRef = new HealthPotion(new PhysicsVector(), new PhysicsVector(), new PhysicsVector(), 6)
val potion6: Any = new HealthPotion(new PhysicsVector(), new PhysicsVector(), new PhysicsVector(), 6)
Lecture Question
Question: in a package named “oop.electronics”, implement the following. This functionality is similar to the last lecture question
- class Battery with
- A constructor that takes a variable named “charge” of type Int
- abstract class Electronic with
- A constructor that takes no parameters
- A state variable named “battery” of type Battery
- A method named “use” that takes no parameters and returns Unit (This can be abstract)
- A method named “replaceBattery” that takes a Battery as a parameter and returns a Battery
- This method swaps the input Battery with the Battery currently stored in this Electronic’s state variable
- The returned Battery is the one that was in the state variable when the method is called
- class Flashlight that extends Electronic
- A constructor that takes no parameters
- When a new Flashlight is created, assign the inherited state variable named “battery” to a new Battery with 5 charge (ie. Batteries included)
- Override the “use” method to reduce the charge of the battery in the state variable by 1 if its charge is 1 or greater
- A constructor that takes no parameters
- class BoomBox that extends Electronic
- A constructor that takes a variable of type Battery and assigns it to the inherited state variable named “battery”
- Your BoomBox constructor parameter should have a different name than the state variable
- Override the “use” method to reduce the charge of the battery in the state variable by 3 if its charge is 3 or greater
- A constructor that takes a variable of type Battery and assigns it to the inherited state variable named “battery”
Hint
The code is in a different package so it doesn’t interfere with your code from the previous question. Be sure you check that this import works from a different package in your project
1 | import oop.electronics.{Battery, BoomBox, Flashlight, Electronic} |
Your Flashlight and BoomBox classes must inherit Electronic. This will be checked by storing them in variables of type Electronic
1 | val flashlight1: Electronic = new Flashlight() |