🔌 인터페이스
📖 인터페이스란?
**인터페이스(Interface)**는 클래스가 구현해야 할 기능의 명세입니다. "무엇을 할 수 있는가"를 정의합니다.
💡 기본 인터페이스
인터페이스 정의
interface Drawable {
fun draw() // 추상 메서드
}
class Circle : Drawable {
override fun draw() {
println("원을 그립니다")
}
}
class Rectangle : Drawable {
override fun draw() {
println("사각형을 그립니다")
}
}
fun main() {
val shapes: List<Drawable> = listOf(Circle(), Rectangle())
for (shape in shapes) {
shape.draw()
}
// 원을 그립니다
// 사각형을 그립니다
}
프로퍼티
interface Named {
val name: String // 추상 프로 퍼티
}
class Person(override val name: String) : Named
class Company(override val name: String) : Named
fun main() {
val person = Person("홍길동")
val company = Company("ABC회사")
println(person.name) // 홍길동
println(company.name) // ABC회사
}
🎨 기본 구현
메서드 기본 구현
interface Clickable {
fun click() // 추상 메서드
fun showMessage() { // 기본 구현
println("클릭됨!")
}
}
class Button : Clickable {
override fun click() {
println("버튼 클릭")
}
// showMessage는 오버라이드 안 해도 됨
}
fun main() {
val button = Button()
button.click() // 버튼 클릭
button.showMessage() // 클릭됨!
}
프로퍼티 기본 구현
interface Named {
val name: String
get() = "이름 없음" // 기본값
}
class Person(override val name: String) : Named
class Anonymous : Named // 기본값 사용
fun main() {
val person = Person("홍길동")
val anon = Anonymous()
println(person.name) // 홍길동
println(anon.name) // 이름 없음
}
🔧 다중 구현
여러 인터페이스 구현
interface Flyable {
fun fly() {
println("날아갑니다")
}
}
interface Swimmable {
fun swim() {
println("수영합니다")
}
}
class Duck : Flyable, Swimmable {
// 두 인터페이스 모두 구현
}
fun main() {
val duck = Duck()
duck.fly() // 날아갑니다
duck.swim() // 수영합니다
}
충돌 해결
interface A {
fun show() {
println("A의 show")
}
}
interface B {
fun show() {
println("B의 show")
}
}
class C : A, B {
override fun show() {
super<A>.show() // A의 메서드 호출
super<B>.show() // B의 메서드 호출
println("C의 show")
}
}
fun main() {
val c = C()
c.show()
// A의 show
// B의 show
// C의 show
}
🎯 실전 예제
결제 시스템
interface PaymentMethod {
fun pay(amount: Int): Boolean
fun refund(amount: Int): Boolean
fun getPaymentInfo(): String {
return "결제 방식 정보"
}
}
class CreditCard(private val cardNumber: String) : PaymentMethod {
override fun pay(amount: Int): Boolean {
println("신용카드 ${cardNumber}로 ${amount}원 결제")
return true
}
override fun refund(amount: Int): Boolean {
println("신용카드로 ${amount}원 환불")
return true
}
override fun getPaymentInfo(): String {
return "신용카드: $cardNumber"
}
}
class BankTransfer(private val accountNumber: String) : PaymentMethod {
override fun pay(amount: Int): Boolean {
println("계좌 ${accountNumber}에서 ${amount}원 이체")
return true
}
override fun refund(amount: Int): Boolean {
println("계좌로 ${amount}원 환불")
return true
}
}
fun processPayment(method: PaymentMethod, amount: Int) {
println(method.getPaymentInfo())
if (method.pay(amount)) {
println("결제 완료!")
}
}
fun main() {
val card = CreditCard("1234-5678")
val transfer = BankTransfer("123-456-789")
processPayment(card, 50000)
println()
processPayment(transfer, 30000)
}
알림 시스템
interface Notifiable {
fun sendNotification(message: String)
}
class EmailNotifier(private val email: String) : Notifiable {
override fun sendNotification(message: String) {
println("이메일 전송 to $email: $message")
}
}
class SmsNotifier(private val phoneNumber: String) : Notifiable {
override fun sendNotification(message: String) {
println("SMS 전송 to $phoneNumber: $message")
}
}
class PushNotifier(private val deviceId: String) : Notifiable {
override fun sendNotification(message: String) {
println("푸시 알림 to $deviceId: $message")
}
}
class NotificationManager(private val notifiers: List<Notifiable>) {
fun notifyAll(message: String) {
for (notifier in notifiers) {
notifier.sendNotification(message)
}
}
}
fun main() {
val manager = NotificationManager(
listOf(
EmailNotifier("user@example.com"),
SmsNotifier("010-1234-5678"),
PushNotifier("device-123")
)
)
manager.notifyAll("새로운 메시지가 도착했습니다!")
}
로깅 시스템
interface Logger {
fun log(level: String, message: String)
fun info(message: String) {
log("INFO", message)
}
fun error(message: String) {
log("ERROR", message)
}
}
class ConsoleLogger : Logger {
override fun log(level: String, message: String) {
println("[$level] $message")
}
}
class FileLogger(private val filename: String) : Logger {
override fun log(level: String, message: String) {
println("$filename에 기록: [$level] $message")
}
}
class MultiLogger(private val loggers: List<Logger>) : Logger {
override fun log(level: String, message: String) {
for (logger in loggers) {
logger.log(level, message)
}
}
}
fun main() {
val logger = MultiLogger(
listOf(
ConsoleLogger(),
FileLogger("app.log")
)
)
logger.info("애플리케이션 시작")
logger.error("오류 발생!")
}
🆚 인터페이스 vs 추상 클래스
비교
// 인터페이스 - "할 수 있는" 기능
interface Flyable {
fun fly()
}
// 추상 클래스 - "무엇인가"
abstract class Animal(val name: String) {
abstract fun sound()
}
// 함께 사용
class Bird(name: String) : Animal(name), Flyable {
override fun sound() {
println("짹짹")
}
override fun fly() {
println("날아갑니다")
}
}
선택 기준
// ✅ 인터페이스
// - 여러 개 구현 가능
// - 기능 명세 (can-do)
interface Clickable {
fun click()
}
// ✅ 추상 클래스
// - 하나만 상속 가능
// - 공통 구현 (is-a)
abstract class Shape {
abstract fun area(): Double
// 공통 구현
fun describe() {
println("면적: ${area()}")
}
}
🤔 자주 묻는 질문
Q1. 언제 인터페이스를 쓰나요?
A: "할 수 있는" 기능을 정의할 때!
// ✅ Clickable - 클릭할 수 있는
interface Clickable {
fun click()
}
// ✅ Drawable - 그릴 수 있는
interface Drawable {
fun draw()
}
// ✅ Comparable - 비교할 수 있는
interface Comparable<T> {
fun compareTo(other: T): Int
}
Q2. 프로퍼티에 setter도 가능한가요?
A: 가능하지만 backing field는 안 됩니다!
interface Resizable {
var width: Int // getter/setter 추상
val height: Int // getter만
// ❌ backing field 불가
// var size: Int = 0
}
class Box : Resizable {
override var width: Int = 0
override val height: Int = 0
}
Q3. 인터페 이스에 상태를 가질 수 있나요?
A: 아니요! 상태는 클래스에만!
interface Counter {
// ❌ 상태(backing field) 불가
// var count: Int = 0
// ✅ 계산 프로퍼티는 가능
val doubled: Int
get() = count * 2
val count: Int // 추상 프로퍼티 (클래스에서 구현)
}
🎬 마치며
인터페이스로 유연한 설계를 만드세요!
핵심 정리:
✅ interface로 계약 정의
✅ 다중 구현 가능
✅ 기본 구현 제공 가능
✅ "할 수 있는" 기능 명세
✅ 다형성 활용
다음 단계: 제네릭에서 타입 파라미터를 알아보세요!