인터페이스(Interface)란 무엇인가
클래스를 이용하여 다중 상속을 할 때 여러 문제가 발생할 수 있기에 자바에서는 인터페이스를 통해 다중 상속을 지원한다.
- 다른 클래스를 작성할 때 기본이 되는 틀을 제공
- 구현된 것은 아무것도 없는 기본 설계도와 같음
- 다른 클래스 사이의 중간 매개 역할까지 담당하는 일종의 추상 클래스
- 오로지 추상 메소드와 상수만을 포함 가능
인터페이스 선언하기
접근제어자 interface 인터페이스이름 {
public static final 타입 상수이름 = 값;
...
public abstract 메소드이름(매개변수목록);
...
}
- 인터페이스의 모든 필드는 public static final
- 인터페이스의 모든 메소드는 public abstract
인터페이스 구현하기
인터페이스는 추상 클래스와 마찬가지로, 자신이 직접 인스턴스 생성하는 것은 불가
따라서, 인터페이스가 포함하고 있는 추상 메소드를 구현해 줄 클래스를 작성한다
class 클래스이름 implements 인터페이스이름 { ... }
만약 모든 추상 메소드를 구현하지 않으면 abstract 키워드를 이용해 추상 클래스로 선언해야 한다
인터페이스 활용 예제
interface Animal { public abstract void cry(); }
class Cat implements Animal {
public void cry() {
System.out.println("냐옹냐옹!");
}
}
class Dog implements Animal {
public void cry() {
System.out.println("멍멍!");
}
}
public class Polymorphism03 {
public static void main(String[] args) {
Cat c = new Cat();
Dog d = new Dog();
c.cry();
d.cry();
}
}
- 실행 결과
- 냐옹냐옹! 멍멍!
interface Animal { public abstract void cry(); }
interface Pet { public abstract void play(); }
class Cat implements Animal, Pet {
public void cry() {
System.out.println("냐옹냐옹!");
}
public void play() {
System.out.println("쥐 잡기 놀이하자~!");
}
}
class Dog implements Animal, Pet {
public void cry() {
System.out.println("멍멍!");
}
public void play() {
System.out.println("산책가자~!");
}
}
public class Polymorphism04 {
public static void main(String[] args) {
Cat c = new Cat();
Dog d = new Dog();
c.cry();
c.play();
d.cry();
d.play();
}
}
- 실행 결과
- 냐옹냐옹! 나비야~ 쥐 잡기 놀이하자~! 멍멍! 바둑아~ 산책가자~!
interface Pet {
var category : String // 추상 프로퍼티
fun feeding() // 추상 메소드
fun patting() { // 구현부를 포함할 수 있다. 구현부를 포함하면 일반 메소드
println("Keep patting")
}
}
class Cat(override var category : String) : Pet { // 주생성자를 이용
override fun feeding() {
println("Feeding 메소드가 구현되었습니다.")
}
}
fun main() {
val obj = Cat("Small")
obj.feeding() // 구현된 메소드
obj.patting()// 일반 메소드
}
package com.journaldev.retrofitintro;
import com.journaldev.retrofitintro.pojo.MultipleResource;
import com.journaldev.retrofitintro.pojo.User;
import com.journaldev.retrofitintro.pojo.UserList;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.GET;
import retrofit2.http.POST;
import retrofit2.http.Query;
interface APIInterface {
@GET("/api/unknown")
Call<MultipleResource> doGetListResources();
@POST("/api/users")
Call<User> createUser(@Body User user);
@GET("/api/users?")
Call<UserList> doGetUserList(@Query("page") String page);
@FormUrlEncoded
@POST("/api/users?")
Call<UserList> doCreateUserWithField(@Field("name") String name, @Field("job") String job);
}
- API 인터페이스 정의API 정의인터페이스 선언사용할 API 한개에 하나의 메소드를 추가GET 메소드 이므로 @GET을 추가한다.
username 매개 변수를 인수에 추가@Path ( "username" ) username: String
Query임을 나타내는 @Query 추가@Query ( "sort" ) sort: String
- API 쿼리를 인수에 추가
- Path의 일부인 것을 나타내는 @Path 추가
- 그리고 API 경로를 설정한다
- @GET ( "users/{username}/repos" )
- @GET ( "users/{username}/repos" ) fun loadRepos( @Path ( "username" ) username: String , @Query ( "sort" ) sort: String ): Call<ResponseBody>
- interface GitHubService { ︙ }
- interface GitHubService {@GET ("users/{username}/repos" ) fun loadRepos( @Path ("username" ) username:String ,@Query ("sort" ) sort:String ): Call<ResponseBody> }
추상 클래스 VS 인터페이스
추상 클래스와 인터페이스는 엄밀히 다르다!
대략적인 설계 명세를 구현하고 인터페이스를 상속하는 하위클래스에서 이를 구체화하는 것까지는 같다.
하지만 인터페이스에서는 프로퍼티의 상태 정보를 저장할 수 없다.
즉, 인터페이스에서는 프로퍼티의 초기화가 불가능하다
추상 클래스
구체화되지 않은 클래스로, 일반적인 객체를 생성하는 방법으로 인스턴스화 될 수 없다
수퍼클래스에서 구현이 끝난 함수를 override 로 재구현하기
fun main() {
var t = Tiger()
t.eat()
}
open class Animal {
open fun eat() {
println("음식을 먹습니다")
}
}
class Tiger : Animal() {
override fun eat() {
println("고기를 먹습니다")
}
}
[음식을 먹습니다] 대신 override를 사용하여 [고기를 먹습니다]로 출력한다
💡 슈퍼클래스에서는 함수의 구체적인 구현은 없고 서브클래스에서 eat()이라는 함수가 반드시 있어야 한다는 점만 명시한다!
각 서브클래스가 필요에 따라 함수의 내용을 구현하도록 하기 위해 추상화(abstraction) 를 사용한다!
추상화
선언부만 있고 기능이 구현되지 않은 추상함수와
추상함수를 포함하는 추상클래스로 구성된다
fun main() {
}
abstract class Animal {
abstract fun eat()
}
추상 함수 eat() : 함수 내용은 적지 않는다! 비어있는 껍데기
추상 클래스 Animal : 미완성 클래스이므로 단독으로 인스턴스를 만들 수 없다
→ 반드시 서브 클래스에서 상속을 받아 abstract 표시가 된 함수들을 구현해주어야 한다!
fun main() {
var r = Rabbit()
r.eat()
r.sniff()
}
abstract class Animal {
abstract fun eat()
fun sniff() {
println("킁킁")
}
}
class Rabbit : Animal() {
override fun eat() {
println("당근을 먹습니다")
}
}
당근을 먹습니다
킁킁
추상화를 하는 또 다른 방법 : 인터페이스
코틀린에서의 인터페이스는 속성, 추상함수, 일반함수를 모두 가질 수 있다
추상함수는 생성자를 가질 수 있는 반면 인터페이스는 생성자를 가질 수는 없다
구현부가 있는 함수 → open 함수로 간주
구현부가 없는 함수 → abstract 함수로 간주
별도의 키워드가 없어도 포함된 모든 함수를 서브클래스에서 구현 및 재정의가 가능하다
또한, 한번에 여러 인터페이스를 상속받을 수 있어 좀 더 유연한 설계가 가능하다
인터페이스 2개를 동시에 상속받는 코드
fun main() {
var d = Dog()
d.run()
d.eat()
}
interface Runner {
fun run()
}
interface Eater {
fun eat() {
println("음식을 먹습니다")
}
}
class Dog : Runner, Eater {
override fun run() {
println("우다다다 뜁니다")
}
override fun eat() {
println("허겁지겁 먹습니다")
}
}
주의! 여러 개의 인터페이스나 클래스에서 같은 이름과 형태를 가진 함수를 구현하고 있다면 서브 클래스에서는 혼선이 일어나지 않도록 오버라이딩하기
오버라이딩 : 이미 구현된 함수를 서브클래스에서 변경해야 할 때
추상화 : 형식만 선언하고 실제 구현은 서브클래스에 일임할 때
인터페이스 : 서로 다른 기능들을 여러 개 물려주어야 할 때
'개발일지' 카테고리의 다른 글
[Kotlin] 필드와 변수, 데이터 타입, 늦은 초기화 (0) | 2023.03.21 |
---|---|
코틀린(Kotlin) 이모저모 공부[변수, 자료형, 형변환, 배열, 함수, 흐름제어, object] (0) | 2023.01.13 |
코드리뷰와 테스트 (0) | 2023.01.03 |
Git-flow (1) | 2023.01.03 |
[코딩애플] 리액트 기초 #1 (0) | 2022.09.20 |