컴굥일지
[Kotlin] 코틀린 객체 지향 프로그래밍 기초 본문
반응형
코틀린 객체 지향 프로그래밍 기초
OOP 소개
- OOP : Object Oriented Programming
- ex) Java, C#, Kotlin...
- 5가지 기본 개념
- 변수와 타입
- 변수 : 저장 공간에서의 위치 (저장 공간을 나타내기 위해 고유의 이름이 필요 -> 이름으로 데이터에 접근)
- 흐름 제어
- 함수
- 코드를 분리해준다.
- 코드를 필요할 때 불러 쓸 수 있다.
=> 재사용이 쉽다
- colletions
- 많은 요소를 한 군데에 저장해준다.
- 반복을 쉽게 사용할 수 있게 해준다.
- (상속을 포함한) 클래스와 객체
- 직접 데이터 타입을 만들게 해준다.
- 데이터 멤버와 메소드를 한 곳에 있게 해준다.
- 가독성있고 유지 가능한 코드를 쓸 수 있게 해준다.
- 변수와 타입
클래스와 객체
- 클래스 != 객체
- 클래스 : 속성와 기술을 정의해주는 도안
- 객체 : 클래스로 만든 무언가
클래스와 초기화
fun main() {
var Jini = Person("Jingyeong","Lee")
var john =Person()
var peter=Person(lastName = "Peter")
}
class Person2 // 이 자체로도 유효함
// constructor는 객체 생성시 값을 추가하게 해준다 (선택임)
// private, public 등을 추가할 수도 있음
class Person(firstName:String = "John", lastName:String="Doe"){ // default 값을 줄 수도 있다
init { // Initializer : 객체가 생성되는 순간 호출되는 것
println("$firstName created")
}
}
멤버 변수 - 기능과 생성자
- 코틀린의 클래스는 하나의 primary constructor와 다수의 secondary constructor를 가질 수 있습니다.
- 그리고 primary constructor의 경우 클래스 헤더(클래스 이름 오른편)에 위치합니다.
- primary constructor에서는 어떤 실행문도 포함될 수 없습니다.
만약 초기화하는 코드를 넣고 싶을 경우 init 블럭을 이용하면 가능합니다. - 그리고 primary constructor는 secondary constructor의 첫 번째 실행문으로 실행됩니다.
- 따라서 init 블럭의 코드는 항상 secondary constructor의 body보다 먼저 실행됩니다.
// constructor는 객체 생성시 값을 추가하게 해준다 (선택임)
// private, public 등을 추가할 수도 있음
class Person(firstName: String = "John", lastName: String = "Doe") { // default 값을 줄 수도 있다
// Member Variables - Properties
var age: Int? = null
var hobby: String = "watch Netflix"
init { // Initializer : 객체가 생성되는 순간 호출되는 것
println("$firstName created")
// println("My age is $age") // constructor로 입력 받은 것은 출력되지 않는다
}
// Member secondary Constructor
constructor(firstName: String, lastName: String, age: Int) : this(firstName, lastName) {
this.age=age
}
// Member functions - Methods
fun stateHobby() {
println("My hobby is $hobby")
// println("$firstName") <-- 여기서는 안 보임. init과 constructor에서만 보임
// 멤버 변수에 선언을 하고 추가하는 과정이 필요함
}
}
늦은 초기화와 setter, getter
fun main() {
var myCar=Car()
println("myCar brand: ${myCar.myBrand}")
myCar.maxSpeed=240 // set value
println("Max Speen id ${myCar.maxSpeed}") // get value
// myCar.myModel=~~ <-- private이라 에러
println(myCar.myModel)
}
class Car(){
// lateinit 나중에 초기화 하겠다는 표시
// 변수를 초기화하기 전에 사용하지 않는 것을 주의하자
lateinit var owner:String
val myBrand:String ="BMW"
// custom getter
get(){
return field.lowercase()
}
var maxSpeed:Int =250
//아래 get,set은 코틀린이 자동으로 생성 - 우리가 직접 쓸 필요 없음
// get()=field
// set(value){
// field=value
// }
// field는 getter, setter 메서드 안에서 프로퍼티 참조를 도와주며 뒷받침 하는 필드이다. 이는 필수이다.
// 만약 getter, setter 안에서 프로퍼티를 입력하면, 반복되는 호출이 일어나서 스택 오버플로우 현상이 일어나게 된다.
// value는 변수에 정해지는 값이다.
//custom setter
set(value){
field = if(value>0) value else throw java.lang.IllegalArgumentException("Max Speen can not be less than 0")
}
var myModel: String ="M5"
private set // 같은 클래스 내에서만 쓸 수 있음
init{
this.myModel="M3" //private 이어도 이건 가능
this.owner="Frank"
}
}
데이터 클래스
//매개변수 1개는 반드시 필요 (var이나 val로 넣어야함)
// abstract, open, sealed, inner 클래스일 수 없다
data class User(val id:Long, var name:String)
fun main() {
val user1=User(1,"Denis")
user1.name="Michael"
val user2=User(1,"Michael")
println(user1.equals(user2))
println(user1==user2)
println("User Details: $user1")
val updatedUser=user1.copy(name = "Denis Panjuta") // 카피할 수 있음. (하면서 수정 가능)
println(user1)
println(updatedUser)
val updatedUser2=user1 // 이렇게 바꾸면 동일해짐
updatedUser2.name="Jini"
println(user1)
println(updatedUser2)
println(updatedUser.component1()) // 1
println(updatedUser.component2()) // Denis Panjuta
val (id, name) =updatedUser
println("id=$id, name=$name")
}
상속 (Inheritance)
// 코틀린에서 클래스느 기본적으로 default라서, 따로 open을 넣어줘야 상속이 가능하다
// sealed를 넣으면 상속할 수 없다
// 모든 클래스는 Any라는 클래스를 상속받는다
// Super/Parent/Base Class
open class Car(val name:String, val brand:String){ // open이어야지 상속이 될 수 있다.(부모)
open var range:Double = 0.0 // 프로퍼티가 open이어야 자식에서 수정 가능
fun extendRange(amount:Double){
if(amount>0) range+=amount
}
open fun drive(distance:Double){
println("Drove for $distance KM")
}
}
// Sub/Child/Derived class of Car
// 상속받는 클래스의 특성은 다 가져야 하지만, 추가할 수는 있다
class ElectricCar(name:String, brand: String, batteryLife:Double): Car(name,brand){
override var range=batteryLife*6
override fun drive(distance: Double) {
println("Drove for $distance Km on electricity")
}
fun drive(){
println("Drove for $range Km on electricity")
}
}
fun main(){
var myCar=Car("A3", "Audi")
var myECar=ElectricCar("S-Model", "Tesla", 85.0)
myCar.drive(200.0)
// Polymorphism(다형성) : 비슷한 특성을 가진 객체들이 공통된 방법으로 여겨지는 것
myECar.drive(200.0)
myECar.drive() // 선택 가능
}
인터페이스
- 인터페이스는 클래스 기능을 확장하게 해준다
- 인터페이스는 기본적으로 클래스가 서명할 수도 있는 계약서이다.
서명하면, 클래스는 인터페이스의 프로퍼티와 함수 구현을 제공할 의무를 가진다. - 왜 사용할까?
- 나중에 구현하고 싶은 특정 함수와 클래스 프로퍼티가 있다면 유용하다. (지금 당장 구현하고 싶지 않은 경우)
- 구체적인 함수 body를 아직 만들고 싶지 않을 때
// 인터페이스는 모든 프로퍼티와 함수를 구현해줄 수 있지만, 꼭 그래야 하는 것은 아니다
// 그리고 구현된 부분은, 이 인터페이스를 사용하는 클래스가 오버라이딩 할 수도 있다
interface Drivable{
val maxSpeed: Double
fun drive():String // 함수 body 없이 header만 존재 - 클래스에서 구현해줘야 함
fun brake(){
println("The drivable is braking")
}
}
// Super/Parent/Base Class
// override 하려면 키워드 추가해야 함
open class Car(override val maxSpeed:Double, val name:String, val brand:String): Drivable{
open var range:Double = 0.0 // 프로퍼티가 open이어야 자식에서 수정 가능
fun extendRange(amount:Double){
if(amount>0) range+=amount
}
// override fun drive(): String = "driving the interface drive" <-- 이렇게 적어도 동일함함
override fun drive(): String {
return "driving the interface drive"
}
open fun drive(distance:Double){ //return이 string이 아니라서 위에 것을 만들어야 함
println("Drove for $distance KM")
}
}
// Sub/Child/Derived class of Car
// 상속받는 클래스의 특성은 다 가져야 하지만, 추가할 수는 있다
class ElectricCar(maxSpeed: Double, name:String, brand: String, batteryLife:Double): Car(maxSpeed,name,brand){
override var range=batteryLife*6
override fun drive(distance: Double) {
println("Drove for $distance Km on electricity")
}
override fun drive():String{ //<-- 인터페이스로부터 온 것이 되니까 오버라이드 해야함
return "Drove for $range Km on electricity"
}
override fun brake() {
super.brake()
println("This is sub")
}
}
추상 클래스
- abstract 클래스는 객체를 만들 수 없다.
- sub 클래스에서 상속은 받을 수 있다.
- 추상 클래스에서 프로퍼티와 메서드는 abstract 키워드를 따로 사용하지 않는 한 추상적이지 않다.
인터페이스와 추상 클래스의 차이
- 인터페이스는 state를 저장할 수 없고 여러 인터페이스를 구현할 수 있지만, 클래스는 한개만 상속받을 수 있다
- 추상클래스는 생성자가 있지만, 인터페이스는 없다.
- 인터페이스는 필드를 저장하지 못한다.
- 추상 클래스는 인터페이스가 추가할 수 있는건 모두 할 수 있다. (필드와 생성자 추가)
=> 그래서 state가 잘 저장된다// An abstract class cannot be instantiated // (you cannot create objects of an abstract class). // However, you can inherit subclasses from can them. // The members (properties and methods) of an abstract class are non-abstract // unless you explicitly use the abstract keyword to make them abstract. abstract class Mammal(val name: String, val origin: String, val weight: Double) { // Concrete (Non Abstract) Properties // Abstract Property - 자식 클래스에서 반드시 override 해야 함 abstract var maxSpeed: Double // Abstract Methods (Must be implemented by Subclasses) abstract fun run() abstract fun breath() // Concrete (Non Abstract) Method fun displayDetails() { println("Name: $name, Origin: $origin, Weight: $weight, " + "Max Speed: $maxSpeed") } } class Human(name: String, origin: String, weight: Double, override var maxSpeed: Double): Mammal(name, origin, weight) { override fun run() { // Code to run println("Runs on two legs") } override fun breath() { // Code to breath println("Breath through mouth or nose") } } class Elephant(name: String, origin: String, weight: Double, override var maxSpeed: Double): Mammal(name, origin, weight) { override fun run() { // Code to run println("Runs on four legs") } override fun breath() { // Code to breath println("Breath through the trunk") } } fun main() { val human = Human("Denis", "Russia", 70.0, 28.0) val elephant = Elephant("Rosy", "India", 5400.0, 25.0) //val mammal = Mammal("Denis","Russia",70.0, 28.0) <-- 추상 클래스는 객체 생성 불가능 human.run() elephant.run() human.breath() elephant.breath() }
형변환
fun main() {
val obj1:Any ="I have a dream"
if(obj1 !is String){
println("not a string")
}else{
println("string") // 자동으로 스트링으로 캐스트 됨
println(obj1.length)
}
// as 키워드를 사용하여 explicit unsafe 캐스팅을 함
// string이 아니면 문제가 생긴다
var str1:String =obj1 as String
println(str1.length)
var obj2:Any =1337
//val str2:String = obj2 as String // string이 아닌데 string으로 형 변환 하려 해서 에러
//println(str2)
// as? 키워드를 사용하여 explicit safe 캐스팅을 함함
var obj3:Any = 1337
val str3 :String? =obj3 as? String
println(str3)
}
반응형
'프로그래밍 강의 > Kotlin' 카테고리의 다른 글
[Kotlin] 코틀린 기초 더 배우기 (0) | 2022.12.28 |
---|---|
[Kotlin] 코틀린 기초 (1) | 2022.12.28 |
Comments