educative - kotlin - 8
48725 ワード
Class Hierarchies and Inheritance
Creating interfaces
いくつかの抽象的な方法からなるインタフェースを作成します!
cottlinインタフェースは
interface
キーワード定義を使用します.interface Remote {
fun up()
fun down()
fun doubleUp() {
up()
up()
}
}
上で作成したインタフェースを利用!デフォルトのジェネレータの横に、使用するインタフェースを示すコロン(:)を付けます.class TV {
var volume = 0
}
class TVRemote(val tv: TV) : Remote {
override fun up() { tv.volume++ }
override fun down() { tv.volume-- }
}
TVRemoteクラスは、リモートインタフェースで定義された抽象メソッドを強制的に上書きする必要があります.しかし、ここでは、上記のdoubleUp()
メソッドは例外である.コトリンのインタフェースは抽象メソッドだけでなくdefaultメソッドも実現できるからである.ここで、up(), down()
メソッドは抽象メソッドと見なされ、上書きの義務があり、```doubleUp()メソッドはdefaultメソッドと見なされ、上書きの義務はない.上記のリモートインタフェースは、Java 8からの
default
キーワードを使用して実現される.public interface Remote {
void up(); // abstract
void down(); // abstract
default void doubleUp() {
up();
up();
}
}
再整理してから使うclass TV {
var volume = 0
}
class TVRemote(val tv: TV) : Remote {
override fun up() { tv.volume++ }
override fun down() { tv.volume-- }
}
val tv = TV()
val remote: Remote = TVRemote(tv) //Remote 인터페이스를 구현한 TVRemote 인스턴스 생성
println("Volume: ${tv.volume}") //Volume: 0
remote.up()
println("After increasing: ${tv.volume}") //After increasing: 1
remote.doubleUp()
println("After doubleUp: ${tv.volume}") //After doubleUp: 3
Javaでは、インタフェースは静的メソッドであってもよいが、cottinではない.Cottlinでインタフェースの静的メソッドを実装する場合は、パートナーオブジェクトを使用します.リモートインタフェースを継承し、combine()
メソッドによって2つのRemote
クラスの演算を実行します.companion object {
fun combine(first: Remote, second: Remote): Remote = object: Remote {
override fun up() {
first.up()
second.up()
}
override fun down() {
first.down()
second.down()
}
}
}
// 사용
val tv = TV()
val remote: Remote = TVRemote(tv)
val anotherTV = TV()
val anotherRemote: Remote = TVRemote(anotherTV)
val combinedRemote = Remote.combine(remote, anotherRemote)
combinedRemote.up()
println(tv.volume) //1
println(anotherTV.volume) //1
Creating abstract classes
abstract
キーワードをクラスの前に貼り付け、抽象クラスを実現できます.abstract class Musician(val name: String, val activeFrom: Int) {
abstract fun instrumentType(): String
}
class Cellist(name: String, activeFrom: Int) : Musician(name, activeFrom) {
override fun instrumentType() = "String"
}
val ma = Cellist("Yo-Yo Ma", 1961)
println(ma.name) // Yo-Yo Ma
println(ma.instrumentType()) // String
インタフェースと抽象クラスの違いは?複数のクラスで
Nested and Inner Classes
内部classを使用して、より効率的な構造を実現しましょう.
class TV {
private var volume = 0
val remote: Remote
get() = TVRemote()
override fun toString(): String = "Volume: ${volume}"
// TVRemote를 TV의 inner class로 옮김
inner class TVRemote : Remote {
override fun up() { volume++ } //바로 outer class property 접근 가능
override fun down() { volume-- }
override fun toString() = "Remote: ${this@TV.toString()}"
}
}
val tv = TV()
val remote = tv.remote
println("$tv") //Volume: 0
remote.up()
println("After increasing: $tv") //After increasing: Volume: 1
remote.doubleUp()
println("After doubleUp: $tv") //After doubleUp: Volume: 3
TVRemote Inner Classを使用すると、up()down()メソッドからTVのプライベート番組に自由にアクセスできます.this@
式を使用してouterclassのメンバーにアクセスできます.Anonymous inner class
anonymous内部クラスでも同様に実現できる.
inner
キーワードはなく、クラス名がない以外はネストされたクラスを使用するのは同じです.class TV {
private var volume = 0
val remote: Remote get() = object: Remote {
override fun up() { volume++ }
override fun down() { volume-- }
override fun toString() = "Remote: ${this@TV.toString()}"
}
override fun toString(): String = "Volume: ${volume}"
}
Inheritance
コトリンの等級と方法は基本的に
final
で、継承できません.したがって、継承できるようにするには、open
キーワードを使用する必要があります.さらに、親クラスのval propertyは、子クラスからvalまたはvarに移行できますが、親クラスのvar propertyは、子クラスからvalに移行できません.通常、valにはgetterがあり、varにはgetterとsetterがあります.したがって、親クラスのvarが子クラスからvalに移行した場合、定義したsetterは親クラスから削除できません.Creating a base class
open class Vehicle(val year: Int, open var color: String) {
open val km = 0
// 상속 가능
final override fun toString() = "year: $year, Color: $color, KM: $km"
// 상속 불가
fun repaint(newColor: String) {
color = newColor
}
}
toString()
メソッドを超えています.repaint()
の方法があります.Creating a derived class
```kotlin
open class Car(year: Int, color: String) : Vehicle(year, color) {
override var km: Int = 0
set(value) {
if (value < 1) {
throw RuntimeException("can't set negative value")
}
field = value
}
fun drive(distance: Int) {
km += distance
}
}
サブクラスCarジェネレータのパラメータは、親クラスVerhicleに移動します.ここで、子は親のkm
%を上書きします.val -> var
はカスタム設定器を実現した.val car = Car(2019, "Orange")
println(car.year) // 2019
println(car.color) // Orange
car.drive(10)
println(car) // year: 2019, Color: Orange, KM: 10 (부모 클래스 toString())
try {
car.drive(-30)
} catch(ex: RuntimeException) {
println(ex.message) // can't set negative value (커스텀 setter)
}
Extending the class
今回はCarを継承するFamilyCarクラスを作成します
class FamilyCar(year: Int, color: String) : Car(year, color) {
override var color: String
get() = super.color
set(value) {
if (value.isEmpty()) {
throw RuntimeException("Color required")
}
super.color = value
}
}
FamilyCarクラスは親クラスVerhicleクラスのcolor propertyをgetterとsetterとして使用します.val familyCar = FamilyCar(2019, "Green")
println(familyCar.color) //Green
try {
familyCar.repaint("") //Custom Setter 발동
} catch(ex: RuntimeException) {
println(ex.message) // Color required
}
上書き時のアクセス制限者の注意事項Sealed class
Sealed classは、同じファイルで親の子クラスタイプを継承することを制限する特性を持つクラスです.
sealed class Card(val suit: String)
class Ace(suit: String) : Card(suit)
class King(suit: String) : Card(suit) {
override fun toString() = "King of $suit"
}
class Queen(suit: String) : Card(suit) {
override fun toString() = "Queen of $suit"
}
class Jack(suit: String) : Card(suit) {
override fun toString() = "Jack of $suit"
}
class Pip(suit: String, val number: Int) : Card(suit) {
init {
if (number < 2 || number > 10) {
throw RuntimeException("Pip has to be between 2 and 10")
}
}
}
fun process(card: Card) = when (card) {
is Ace -> "${card.javaClass.name} of ${card.suit}"
is King, is Queen, is Jack -> "$card"
is Pip -> "${card.number} of ${card.suit}"
}
// when() 구문에서 else는 넣지 않는다 -> 새로운 sealed 클래스가 추가되었을때 오류가 생길 수 있다
fun main() {
println(process(Ace("Diamond"))) // Ace of Diamond
println(process(Queen("Clubs"))) // Queen of Clubs
println(process(Pip("Spades", 2))) // 2 of Spades
println(process(Pip("Hearts", 6))) // 6 of Hearts
}
enum classes
enum classを使用すると、コードを簡略化し、可読性を向上させることができます.
enum class Suit { CLUBS, DIAMONDS, HEARTS, SPADES }
sealed class Card(val suit: Suit)
class Ace(suit: Suit) : Card(suit)
class King(suit: Suit) : Card(suit) {
override fun toString() = "King of $suit"
}
// UseCardWithEnum.kt
println(process(Ace(Suit.DIAMONDS))) // Ace of DIAMONDS
println(process(Queen(Suit.CLUBS))) // Queen of CLUBS
println(process(Pip(Suit.SPADES, 2))) // 2 of SPADES
println(process(Pip(Suit.HEARTS, 6))) // 6 of HEARTS
Suit.DIAMONDS
は、Suitクラスの例のstatic
である.Customizing enums
Enumクラスはカスタマイズと反復が可能です.
// iteration
for (suit in Suit.values()) {
println("${suit.name} -- ${suit.ordinal}")
}
"""
CLUBS -- 0
DIAMONDS -- 1
HEARTS -- 2
SPADES -- 3
"""
// Customizing
enum class Suit(val symbol: Char) {
CLUBS('\u2663'),
DIAMONDS('\u2666'),
HEARTS('\u2665') {
override fun display() = "${super.display()} $symbol"
},
SPADES('\u2660');
open fun display() = "$symbol $name"
}
for (suit in Suit.values()) {
println(suit.display())
}
♣ CLUBS
♦ DIAMONDS
♥ HEARTS ♥
♠ SPADES
Reference
この問題について(educative - kotlin - 8), 我々は、より多くの情報をここで見つけました https://velog.io/@sungjun-jin/educative-kotlin-8テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol