자바에서 코틀린 Companion 객체 사용
@JvmStatic
- 자바에서는 코틀린의 컴패니언 객체를 접근하기 위해 @JvmStatic 애노테이션(annotation) 표기법을 사용
1.
package com.example.pp192
class KCustomer {
companion object {
const val LEVEL = "INTERMEDIATE"
@JvmStatic fun login() = println("Login...") // 어노테이션 표기 사용
fun hahee() = println("hahee..")
}
}
2.
package com.example.pp192;
public class KCustomerAccess {
public static void main(String[] args) {
// 코틀린 클래스의 컴패니언 객체를 접근
System.out.println(KCustomer.LEVEL);
KCustomer.login(); // 어노테이션을 사용할 때 접근 방법
KCustomer.Companion.hahee(); // 위와 동일한 결과로 어노테이션을 사용하지 않을 때 접근 방법
}
}
자바에서 코틀린 컴패니언 객체 사용
Object와 Singleton
상속할 수 없는 클래스에서 내용이 변경된 객체를 생성할 때
- 자바의 경우 익명 내부 클래스를 사용해 새로운 클래스 선언
- 코틀린에서는 object 표현식이나 object 선언으로 이 경우를 좀 더 쉽게 처리
package com.example.pp194
// (1) object 키워드를 사용한 방식
object OCustomer {
var name = "Kildong"
fun greeting() = println("Hello World!")
val HOBBY = Hobby("Basketball")
init {
println("Init!")
}
}
class Hobby(val name: String)
fun main() {
OCustomer.greeting() // 객체의 접근 시점
OCustomer.name = "Dooly"
println("name = ${OCustomer.name}")
println(OCustomer.HOBBY.name)
}
Object 선언과 컴패니언 객체 비교 예제
object 선언 방식은 접근 시점에 객체가 생성된다. 때문에 생성자 호출을 하지 않으므로 object 선언에는 주/부 생성자를 사용할 수 없다. 자바에서는 OCustomer.INSTANCE.getName(); 와 같이 접근해야 한다.
Object 표현식
- object 선언과 달리 이름이 없음.
- 싱글톤이 아님
- object 표현식이 사용될 때마다 새로운 인스턴스가 생성
- 이름이 없는 익명 내부 클래스로 불리는 형태를 object 표현식으로 생성 가능
package com.example.pp196
open class Superman() {
fun work() = println("Taking photos")
fun talk() = println("Talking with people.")
open fun fly() = println("Flying in the air")
}
fun main() {
val pretendedMan = object: Superman() { // object 표현식으로 fly() 구현의 재정의
override fun fly() = println("I'm not a real supernam. I can't fly!")
}
pretendedMan.work()
pretendedMan.talk()
pretendedMan.fly()
}
object 표현식 사용 예제
- 하위 클래스를 만들지 않고도 새로운 구현인 fly()를 포함 가능
- object 표현식으로 fly()구션의 재정의
++ 추상 클래스(abstract class) ++
- 구현 클래스에서 가져야할 명세를 정의한 클래스 (프로퍼티 및 메서드 템플릿)
- abstract라는 키워드와 함께 선언하며 추상클래스는 객체 생성 안됨
- ‘구체적이지 않은 것’을 나타내기 때문에 하위 파생 클래스에서 구체적으로 구현
- open 키워드를 사용하지 않고도 파생 클래스 작성 가능
package com.example.pp198
abstract class Vehicle(val name: String, val color: String, val weight: Double) {
abstract var maxSpeed: Double
var year = "2023"
abstract fun start()
abstract fun stop()
fun displaySpecs() {
println("Name: $name, Color: $color, Weight: $weight, Year: $year, Max Speed: $maxSpeed")
}
}
class Car(name: String, color: String, weight: Double, override var maxSpeed: Double)
: Vehicle(name, color, weight) {
override fun start() {
println("Car Started")
}
override fun stop() {
println("Car Stopped")
}
}
class Motorcycle(name: String, color: String, weight: Double, override var maxSpeed: Double)
: Vehicle(name, color, weight) {
override fun start() {
println("Bike Started")
}
override fun stop() {
println("Bike Stopped")
}
}
fun main() {
val car = Car("SuperMatiz", "yellow", 1110.0, 270.0)
val motor = Motorcycle("DreamBike", "red", 173.0, 100.0)
car.year = "2022"
car.start()
motor.displaySpecs()
motor.start()
}
추상클래스 예제
단일 인스턴스로 객체 생성
- object를 사용한 생성
추상 클래스로부터 하위 클래스를 생성하지 않고 단일 인스턴스로 객체 생성 가능
package com.example.pp201
// 추상 클래스의 선언
abstract class Printer {
abstract fun print() // 추상 메서드
}
val myPrinter = object: Printer() { // 객체 인스턴스
override fun print() { // 추상 메서드의 구현
println("출력합니다.")
}
}
fun main() {
myPrinter.print()
}
단일 인스턴스로 객체 생성 예제
인터페이스
- 생성자가 없다.
- 클래스가 아니므로 다양한 인터페이스로부터 클래스 구현 가능(다중 상속)
- 추상 클래스와는 다르게 강한 연관을 가지지 않는다.
- 다른 언어와는 다르게 기본적인 구현 내용이 포함할 수 있음
- 선언하려면 interface 키워드
- 상속한 하위 클래스에서는 override를 사용해 해당 메서드를 구현
interface 인터페이스명 [: 인터페이스명...] {
추상 프로퍼티 선언
추상 메서드 선언
[일반 메서드 선언 { ... }]
}
** 인터페이스는 상수일때만 게터를 통한 구현이 가능하다.
** 프로퍼티 상수 - 추상
** 인터페이스의 생성자는 없다.
** 인터페이스의 메소드는 총 세가지 지원 1. 추상 메소드 2. 정적 메소드 3. 디폴트 메소드
** 하지만 실무에서는 특별한 경우가 아니면 거의 추상 메소드만 사용
package com.example.pp204
interface Pet {
var category: String // abstract 키워드가 없어도 기본은 추상 프로퍼티
fun feeding() // 마찬가지로 추상 메서드
fun patting() { // 일반 메서드 : 구현부를 포함하면 일반적인 메서드로 기본이 됨
println("Keep patting!") // 구현부
}
}
class Cat(override var category: String) : Pet {
override fun feeding() {
println("Feed the cat a tuna can!")
}
}
fun main() {
val obj = Cat("small")
println("Pet Category: ${obj.category}")
obj.feeding() // 구현된 메서드
obj.patting() // 기본 메서드
}
Pet 인터페이스 예제
package com.example.pp206
interface Pet {
var category: String
val msgTags: String // val 선언 시 게터의 구현이 가능
get() = "I'm your lovely pet!"
fun feeding()
fun patting() {
println("Kepp pattin!")
}
}
class Cat(override var category: String) : Pet {
override fun feeding() {
println("Feed the cat a tuna can!")
}
}
fun main() {
val obj = Cat("small")
println("Pet Message Tags: ${obj.msgTags}")
obj.feeding() // 구현된 메서드
obj.patting() // 기본 메소드
}
Getter를 구현한 프로퍼티.
- 상수일 때만
- 인터페이스에서는 프로퍼티에 값을 저장할 수 없음. val로 선언된 프로퍼티는 게터를 통해 필요한 내용 구현 가능
package com.example.pp207
interface Pet {
var category: String
val msgTags: String // val 선언 시 게터의 구현이 가능
get() = "I'm your lovely pet!"
fun feeding()
fun patting() {
println("Keep patting!")
}
}
open class Animal(val name: String)
// feeding의 구현을 위해 인터페이스 Pet 지정
class Dog(name: String, override var category: String) : Animal(name), Pet {
override fun feeding() {
println("Feed the dog a bone")
}
}
class Cat7(name: String, override var category: String) : Animal(name), Pet {
override fun feeding() {
println("Feed the cat a tuna can!")
}
}
class Master {
fun playWithPet(dog: Dog) { // 각 애완동물 종류에 따라 오버로딩됨
println("Enjoy with my dog.")
}
fun playWithPet(cat: Cat7) { // 고양이를 위한 메서드
println("Enjoy with my cat.")
}
}
fun main() {
val master = Master()
val dog = Dog("Toto", "Small")
val cat = Cat7("Coco", "BigFat")
master.playWithPet(dog)
master.playWithPet(cat)
}
오버로딩 예제
- Master클래스 안의 palyWithPet(...)
package com.example.pp209
interface Pet {
var category: String
val msgTags: String // val 선언 시 게터의 구현이 가능
get() = "I'm your lovely pet!"
fun feeding()
fun patting() {
println("Keep patting!")
}
}
open class Animal(val name: String)
// feeding의 구현을 위해 인터페이스 Pet 지정
class Dog(name: String, override var category: String) : Animal(name), Pet {
override fun feeding() {
println("Feed the dog a bone")
}
}
class Cat7(name: String, override var category: String) : Animal(name), Pet {
override fun feeding() {
println("Feed the cat a tuna can!")
}
}
class Master {
fun playWithPet(dog: Dog) { // 각 애완동물 종류에 따라 오버로딩됨
println("Enjoy with my ${dog.name}.")
}
fun playWithPet(cat: Cat7) { // 고양이를 위한 메서드
println("Enjoy with my ${cat.name}.")
}
}
fun main() {
val master = Master()
val dog = Dog("Toto", "Small")
val cat = Cat7("Coco", "BigFat")
master.playWithPet(dog)
master.playWithPet(cat)
}
여러 인터페이스의 구현
다중 상속
- 클래스는 기본적으로 다중 상속 지원하지 않음
- 인터페이스 여러 개를 하나의 클래스에서 구현하는 것이 가능 -> 다중 상속과 같은 효과
자바는 다중상속 불가
코틀린도 다중상속 불가(자바 기반임)
package com.example.pp211
interface Bird {
val wings: Int
fun fly()
fun jump() = println("bird jump!")
}
interface Horse {
val maxSpeed: Int
fun run()
fun jump() = println("jump!, max speed: $maxSpeed")
}
class Pegasus: Bird, Horse {
override val wings: Int = 2
override val maxSpeed: Int = 100
override fun fly() = println("Fly!")
override fun run() = println("Run!")
override fun jump() {
super<Horse>.jump()
println("and Jump!")
}
}
fun main() {
val pegasus = Pegasus()
var jump = pegasus.jump()
}
다중 상속 예제
데이터 전달을 위한 데이터 클래스
데이터 전달을 위한 객체 DTO(Data Transfer Object)
- 자바에서는 POJO(Plain Old Java Object)
- 구현 로직을 가지고 있지 않고 순수한 데이터 객체를 표현
- 데이터를 접근하는 게터/세터를 포함
- toString(), equals() 등과 같은 데이터 표현 및 비교 메서드
자바로 DTO를 표현하면 데이터 필드들, 게터/세터들, 데이터 표현 및 비교 메서드들을 모두 작성해야 함
코틀린으로 DTO를 표현하면 프로퍼티만 신경 써서 작성하면 나머지는 내부적으로 자동 생성됨
코틀린의 데이터 클래스
- DTO를 표현하기 적합한 클래스 표현으로 data class 키워드를 사용해 정의
코틀린의 프로퍼티 = 필드(변수) + 게터와 세터
- 자동 생성되는 메서드들
- 프로퍼티를 위한 게터/세터
- 비교를 위한 equals()과 키 사용을 위한 hashCode()
- 프로퍼티를 문자열로 변환해 순서대로 보여주는 toString()
- 객체 복사를 위한 copy()
- 프로퍼티에 상응하는 component1(), component2() 등
데이터 클래스 선언
data class Customer(var name: String, var email: String)
- 주 생성자는 최소한 하나의 매개변수를 가짐
- 주 생성자의 모든 매개변수는 val, var로 지정된 프로퍼티여야 함
- 데이터 클래스는 abstract, open, sealed, inner 키워드를 사용할 수 없음
- 간단한 로직을 포함 하려면 부 생성자나 init 블록을 넣어 데이터를 위한 간단한 로직을 포함할 수 있다.
data class Customer(var name: String, var email: String) {
var job: String = "Unknown"
constructor(name: String, email: String, _job: String): this(name, email) {
job = _job
}
init {
// 간단한 로직은 여기에
}
}
method | function |
equals() | 두 객체의 내용이 같은지 비교하는 연산자로 ==와 동일 (고유값은 다르지만 의미값이 같을 때) |
hashCode() | 객체를 구별하기 위한 고유한 정수값 생성, 데이터 셋이나 해시테이블을 사용하기 위한 하나의 생성된 인덱스 |
copy() | 빌더 없이 특정 프로퍼티만 변경해서 객체 복사하기 |
toString() | 데이터 객체를 읽기 편한 문자열로 반환하기 |
componentN() | 객체의 선언부 구조를 분해하기 위해 프로퍼티에 상응하는 메서드 |
package com.example.pp217
data class Customer(var name: String, var email: String) {
var job: String = "Unknown"
constructor(name: String, email: String, _job: String): this(name, email) {
job = _job
}
init {
//
}
}
fun main() {
val cus1 = Customer("Skyler", "skyler@email.com")
val cus2 = Customer("Skyler", "skyler@email.com")
println(cus1 == cus2) // 동등성 비교 true
println(cus1.equals(cus2)) // 위와 동일 true
println("${cus1.hashCode()}, ${cus2.hashCode()}") // 고유값도 동일
}
데이터 클래스의 제공 메서드 - equals(), hashCode() 예제
package com.example.pp218
data class Customer(var name: String, var email: String) {
var job: String = "Unknown"
constructor(name: String, email: String, _job: String): this(name, email) {
job = _job
}
init {
//
}
}
fun main() {
val cus1 = Customer("Skyler", "skyler@email.com")
val cus3 = cus1.copy(name = "Jeniffer") // name만 변경하고자 할 때
println(cus1.toString())
println(cus3.toString())
}
데이터 클래스의 제공 메서드 - copy() 예제
'Kotlin' 카테고리의 다른 글
Kotlin #11 : 제네릭, 배열 230223 (0) | 2023.02.24 |
---|---|
Kotlin #10 : 내부 클래스, 연산자 230223 (0) | 2023.02.23 |
Kotlin #8 : 코틀린의 프로퍼티, 자바의 필드, 지연 초기화, lazy, 위임 230220 (0) | 2023.02.22 |
Kotlin #7 : 정보 은닉 캡슐화 (연관, 의존, 집합, 구성) 230220 (0) | 2023.02.20 |
Kotlin #6 : 상속과 클래스 230217 (0) | 2023.02.20 |
댓글