2022-03-23 TIL

이창호·2022년 3월 23일
0

데브코스 6일차

Interface는 어디에 쓰이나

1. 구현을 강제한다.

구현을 강제한다는건 java에서 implements로 상속받으면 해당 interface의 기능을 전부 사용해야 한다.

interface Hello {
	void hi();
    void bye();
}

class Hi implements Hello {
	@Override
    public void hi() { System.out.println("Hello World"); }
    
    @Override
    public void bye() { System.out.println("Bye World"); }
}

2. 다형성을 제공한다.

다형성을 제공한다는 것은 객체가 여러 형태로 존재할 수 있게 해준다는 것이다.
아래 코드와 같이 Floppy란 객체는 Dog면서도 Friend의 역할을 하는 것이다.

interface Friend {
	void isFriend();
};

interface Dog {
	void name();
};

class Floppy implments Friend, Dog {
	@Override
	public void isFriend() { System.out.println("Yes"); }
    
    @Override
    public void name() { System.out.println("Floppy"); }
}

3. 결합도를 낮춘다. ( DI )

interface Animal {}

interface Dog {}
interface Cat {}

Dog animal = Dog();
Cat animal = Cat();
-----------------------
Animal animal = Dog();
animal = Cat();

위 코드처럼 animal 필드가 구상체(Dog, Cat)과 결합하면 구상체에 의존할 수 밖에 없게 된다. 하지만 추상체(Animal)와 결합하면 추상체에 좀 더 다양한 형을 받을 수 있게 된다.

pblic Animal findAnimal(String name) {
	if(name.equals("dog"){ return new Dog(); } 
    else { return new Cat(); }
}

Animal myFriend = findAnimal("dog");

여기서 Dependency Injection을 볼 수 있다. findAnimal 메서드는 외부에서 오는 값에 따라 다른 객체를 반환한다. 이를 의존성을 외부로 주입받는다고 하여 DI라 부른다.

default Method!

인터페이스가 구현을?

자바 8부터는 인터페이스도 구현체를 가지게 된다. 메서드 앞에 default만 붙이면 끝이다.

interface test {
	default void dm() { System.out.println("Hello World"); }
}

defaualt method 덕분에 가능한 것

상속받는 객체들이 공통적으로 수행하는 것을 default method로 만들어 중복 구현을 하지 않아도 수행할 수 있도록 한다.

interface Sound {
	default void hello() { System.out.println("Hello"); }
    void laugh();
}

class Human implements sound{
	@Override
    public void laugh() { System.out.println("Hahaha"); } 
}

class Dog implements sound{
	@Override
    public void laugh() { System.out.println("???"); } 
}

Sound sound = new Human();
sound.hello();

sound = new Dog();
sound.hello();

위 코드와 같이 Sound 인터페이스에 있는 hello는 Dog, Human 두 객체 모두 같은 기능을 수행하기 때문에 default method로 만들었다.

Functional Interface가 하는 것

Functional Interface란

@FunctionalInterface
interface test {
	void test();
    default void hello() { System.out.println("Hello"); }
    static void bye() { System.out.println("Bye"); }
}

추상 메서드가 하나만 존재하는 인터페이스다. 위 코드처럼 static, default 메서드는 얼마가 있든 상관없다.

익명클래스를 만든다.

Test t = new Test {
	@Override
    public void test() { System.out.println("test"); }
};
t.hello();

위와 같이 익명클래스는 상속받은 클래스가 없이 인터페이스 생성자만으로도 구현할 수 있게 해준다.

Lambda 표현식을 사용할 수 있게 해준다.

Test t = () -> { System.out.println("test"); 

위에 있는 익명클래스 코드를 람다 표현식을 사용하면 1줄로 줄일 수 있다. 람다 표현식은 Funtional Interface에 구현해야 할 추상메서드가 하나 뿐인 점을 이용하여 익명 메서드를 만들어 바로 사용할 수 있게 해준다.

메소드 레퍼런스는 왜 쓸까

public sattic void filterNums(int m, Predicate<Integer> p, Consumer<Integer> c) {
	for (int i = 0; i < max; i++) {
		if (p.test(i)) c.accept(i);
	}
}

filterNums(10, i -> i > 0, i -> System.out.println(i));
filterNums(10, i -> i > 0, System.out::println);

함수를 실행하는 두 코드는 전부 같은 기능을 수행한다. :: 이란 녀석이 붙으면 메소드 레퍼런스 표현 방식을 쓴 것. 이는 어짜피 i를 받아서 i로 출력하니깐 줄여서 쓰기 위해 사용한 것 처럼 보이지만 다른 이유도 있다 한다.

입력값을 변경하지 말라는 표시이기도 하다. 만약 인수인계받고 i를 받아 i를 출력하는 로직이 이상해보여서 i+1를 출력하는 로직으로 바꾼다면 원래 개발했던 의도와 다르게 작동할 수 있다. 이렇듯 개발자의 추가 의도 개입을 차단하는 역할도 한다.

profile
이타적인 기회주의자

0개의 댓글