[Kotlin] 추상 클래스, 추상 메서드, 인터페이스, 람다식 문법 정리

Haeun Noh·2023년 4월 11일
0

Kotlin

목록 보기
1/1
post-thumbnail

0411

요즘 학교에서 방과후로 코틀린 문법을 배우고 있는데 추상 클래스와 추상 메서드, 인터페이스의 개념정리가 필요해보여 글을 작성하고 있습니다.



추상(abstract) 클래스

추상클래스 : 본체가 없고 선언이 목적인 클래스

추상클래스는 무엇일까요?
추상이라는 키워드에서 알 수 있듯이 본체가 없는 메서드를 말합니다.

추상클래스는 클래스 앞에 abstract 키워드를 붙여 지정하고 ;로 끝이 납니다.

또한 추상 클래스는 인스턴스화를 금지하는데요,
인스턴스화란 클래스로 인스턴스를 생성하는 것을 말합니다.


오잉? 그럼 인스턴스를 생성하지도 않고 본체도 없는 추상 메서드는 대체 왜 쓰는 건가요?


사용 목적

사용목적 : 공통적으로 사용되는 기능을 선언하고, 추상클래스를 상속받은 후에 오버라이딩하여 사용

추상클래스는 공통적으로 사용되는 기능을 선언하고, 추상클래스를 상속받은 후에 오버라이딩하여 사용 하기 위해 존재합니다.

"구현한다" 라는 것은 추상 메서드를 오버라이딩하는 것을 의미하죠. 앞으로 많이 나올 표현이니 잘 익혀두자구요.


그렇다면 이제 추상클래스가 무엇인지에 대한 감을 잡기 위해 아래의 예제를 봅시다.

abstract class Animal {
    var name : String = ""
    abstract fun move()
}

class Tiger : Animal() {
    var age : Int = 0
    override fun move() {
        println("네 발로 이동한다.")
    }
}

class Eagle : Animal() {
    var home : String = ""
    override fun move() {
        println("날개로 날아간다.")
    }
    
}

fun main() {
    var tiger : Tiger
    var eagle : Eagle
	 	tiger = Tiger()
    eagle = Eagle()
    tiger.move()
    eagle.move()
}

추상클래스인 Animal이 만들어져 있습니다. 어떻게 알았냐구요? 앞에 붙어있는 abstract를 보면 알 수 있습니다!
Animal 안에 있는 move() 메서드 또한 앞에 abstract가 붙어있어 추상 메서드 라는 것을 단번에 알 수 있습니다.


추상이 붙으면 기능을 정의하는 것에 초점이 맞춰져 있다고 보면 됩니다.
사용을 위한 준비라는 말이죠.


그리고 이렇게 정의된 추상클래스는 상속할 때 반드시 포함되어 있는 추상 메서드를 오버라이딩 즉, 재정의해야 합니다. 꼭 명심하세요! 추상클래스와 추상메서드의 역할이 정의임을 잊지 말자구요.



인터페이스(interface)

인터페이스 - 약속을 지킬 때, 명세화할 때 사용

약속을 지킬 때, 명세화할 때 사용하는 인터페이스도 추상클래스와 성격이 비슷합니다.

interface 키워드를 사용하는 것만 다를 뿐, 정의하고 추상 메서드를 선언하는 것에서는 큰 차이가 없죠.
클래스에서 인터페이스를 받아 완성할 때 상속과 마찬가지로 : 인터페이스 이름 형식을 사용한다는 점에서도 비슷합니다.


그럼 뭐가 다른가요 선생님???
아래의 예제를 보면서 얘기를 나눠봅시다.

abstract class Animal {
    var name : String = ""
    abstract fun move()
}

interface iAnimal {
    abstract fun eat()
}

class iCat : iAnimal {
    override fun eat() {
        println("고양이는 생선을 좋아한다.")
    }
}

class iTiger : Animal(), iAnimal {
    override fun move() {
        println("호랑이가 네 발로 걷는다.");
    }
    override fun eat() {
        println("호랑이는 고기를 좋아한다.")
    }
    fun catchDear() {
        println("호랑이가 사슴을 잡는다.")
    }
}

fun main() {
    
    var cat = iCat()
    var tiger = iTiger()
    cat.eat()
    
    tiger.eat()
    tiger.move()
    tiger.catchDear()
}

아까 추상클래스는 상속받을 때 () 를 꼭 명시해야 했었습니다. 하지만 인터페이스는 ()를 명시하지 않습니다. 주의하세요! 많이 헷갈릴 수 있습니다.


또한 상속의 특성상 iTiger클래스가 Animal()iAnimal() 둘을 동시에 상속받고 있기 때문에 iAnimal인터페이스의 eat() 메서드와 Animal클래스의 move() 메서드 모두를 구현해주어야 하는 모습을 볼 수 있습니다.



람다식 (lambda expression)

혹시 익명 클래스라고 들어보셨나요?
클래스를 구현하면서 객체를 생성해본 경험은요??


혹시 JAVA에서 ActionListener를 써 본 적이 있나요?
익명클래스가 뭔지 감이 잡히지 않을 때, 바로 아래가 익명클래스라고 하면 도움이 되실 겁니다.

ActionListener listener = new ActionListener() {
		public void actionPerformed(ActionEvent e) {
						
		}
}

ActionListener를 사용해본 분이라면 많이 익숙한 구조일텐데요,
자바 또는 안드로이드 같은 경우, 익명 클래스는 이벤트 처리를 할 때 주로 사용합니다.

코틀린에서는 람다식이라는 것을 사용해서 좀 더 코드를 간결하게 만듭니다.


람다식 : 함수를 익명함수(anonymous function) 형태로 간단히 표현 한 것

코드가 간결해져서 가독성이 좋아지는 장점이 있습니다.
람다는 {} 안에 매개변수와 메서드의 모든 내용을 선언하는데요, 아래의 예제를 한 번 봅시다.


밑의 예제는 파라미터 2개를 받아서 합계를 출력하는 일반적인 메서드 방식입니다.

fun addNumber(n1 : Int, n2: Int) : Int {
		return n1 + n2
}

그리고 위의 코드를 아래와 같이 람다식으로 간단히 표현할 수 있습니다.

val addNumber = {n1: Int, n2: Int -> n1 + n2}

훨씬 간결하고 가독성이 좋아지지 않았나요?


람다식의 특징

  1. 람다식은 {}로 감싸며 fun예약어를 사용하지 않음
  2. {} 의 왼쪽은 파라미터 , 오른쪽은 함수의 내용
  3. 오른쪽 문장이 return


제네릭스 (Generics)

제네릭스 : 데이터 형식의 안전성을 보장

자바를 조금 해보신 분이라면 <>이 표현을 보셨을겁니다.
제네릭스가 바로 이 꺽쇠라고 생각하시면 됩니다.


제네릭스데이터 형식의 안전성을 보장하는 데에 사용합니다.
예를 들어 ArrayList문자열만 들어가도록 설정하고 싶다면 <String>과 같이 표현해주면 됩니다.


아래의 예시로 좀 더 정확히 이해해봅시다.

        var strList = ArrayList<String>(4)
        strList.add("첫번째")
        strList.add("첫번째")
        strList.add(3)

이렇게 제네릭스는 데이터 타입을 제한할 수 있지만 사실 제네릭스는 생략할 수 있습니다.

생략하게 되면 object타입이 되어 정수든 문자열이든 자료형을 제한하지 않게 됩니다. 참고해주세요!



profile
기록의 힘을 믿는 개발자, 노하은입니다!

0개의 댓글