인터페이스는 추상메서드와 default 메서드 그리고 static 메서드를 가질 수 있다.
Interface의 특성
java8부터 사용 가능
interface에 구현 메서드 작성 가능
override없이 사용 가능하지만 override도 가능
adapter 역할을 수행 가능. ?????
인터페이스 추가만으로 기능을 확장할 수 있다.
//Interface
interface Flyable {
default void fly() {
System.out.println("FLY");
}
}
interface Swimmable {
default void swim() {
System.out.println("SWIM");
}
}
interface Walkable{
default void walk() {
System.out.println("WALK");
}
}
//Class
class Swan implements Flyable, Walkable, Swimmable{ } //구현을 안해줘도 된다.
//implements로 기능 추가
public class Main {
public static void main(String[] args) {
new Swan().fly();
new Swan().walk();
new Swan().swim(); //implements된 기능들
}
}
static method를 가질 수 있음. → 인터페이스를 통해 메서드 사용 가능 → 함수 제공자가 된다.
//Interface
public interface Ability{
static void sayHello() {
System.out.println("Hello World!");
}
}
//Class
public static void main(String[] args) {
Ability.sayHello();
}
3-1. 인터페이스 임시 생성
//Interface
@FunctionalInterface
public interface MySupply {
String supply();
}
//익명 클래스
new MySupply(){
@Override
public String supply() {
return "Hello World";
}
}.supply();
익명 클래스는 왜! 사용하는가
하나의 메서드만 전달하면 좋을텐데 객체를 생성해서 override까지 해야하다니 귀찮다!
//익명 클래스
new MySupply(){
@Override
public String supply() {
return "Hello World";
}
}.supply();
//Lamda
((MySupply) () -> "Hello World").supply();
4-1. 메서드 레퍼런스
//Interface
@FunctionalInterface
interface MyNumber{
void number(int i);
}
//일반 표현식
MyNumber n = (i) -> System.out.println(i);
//메서드 레퍼런스
MyNumber n = System.out::println; //더 간결한 코드
특정 타입을 미리 지정하지 않고, 필요에 의해 지정할 수 있도록 Generic 타입으로 지정하여
클래스 내부에서 타입을 지정하는 것이 아닌 외부에서 사용자에 의해 지정되는 것을 의미한다.
제너릭은 reference타입만 사용 가능하다. primitive 불가능
장점
사용법
타입 | 설명 |
---|---|
<T> | Type |
<E> | Element |
<K> | Key |
<V> | Value |
<N> | Number |
위 표는 제너릭 타입의 통상적인 룰이다. 물론 위 표대로 사용하지 않아도 된다.
@FunctionalInterface
public interface MyMapper<IN, OUT> {
OUT map(IN in);
}
사용법
class ClassName <T> { }
interface InterfaceName <T> { }
위와같이 이름 다음으로 선언하며 제너릭 타입은 해당 블럭안에서만 유효하다.
1-1. 제너릭 클래스
class ClassName <T> {
private T variable; //제너릭 타입 변수
void set(T variable) { //제너릭 파라미터 메서드
this.variable = variable;
}
T get() { //제너릭 타입 반환
return variable;
}
}
public class Main{
public static void main(String[] args){
ClassName<String> s = new ClassName<String>();
ClassName<Integer> i = new ClassName<Integer>();
s.set("10");
i.set(10);
}
}
위 코드를 실행하면 타입변환이 잘 되어 출력된다
클래스에서 지정한 제너릭 유형과 별도로 메서드에서 독립적인 제너릭 유형을 선언하여 사용할 수 있다.
class ClassName <E> {
<T> T method(T t) {
return t;
}
}
public class Main{
public static void main(String[] args){
ClassName<Integer> i = new ClassName<Integer>();
System.out.println("i.method(3).getClass().getName() = " + i.method(3).getClass().getName());
System.out.println("i.method(\"3\").getClass().getName() = " + i.method("3").getClass().getName());
}
}
원리는 같다.
그럼 static 메서드는 프로그램 실행시에 메모리에 적재되는데 어떻게 타입을 얻어올까?
→ 클래스와 static 메서드가 같은 제너릭을 사용하면 클래스 객체가 생성되기전에는 제너릭이 지정되지 않는다.
타입 제한
제너릭에도 타입을 제한할 수 있다.
<IN extends Child> : Child와 Child의 자손들만 IN에 들어올 수 있다.
<IN super Parent> : Parent와 Parent의 부모들만 IN에 들어올 수 있다.
IN을 생략하고 ?를 쓴다면 본인이 기준이 된다.
<? extends IN> : IN과 IN의 자손만 들어올 수 있다.
<? super IN> : IN과 IN의 부모만 들어올 수 있다.
https://st-lab.tistory.com/153#recentComments
인터페이스에는 추상메서드, default메서드, static메서드를 넣을 수 있다.
추상메서드가 하나인 인터페이스를 FunctionalInterface라고 한다.
FunctionalInterface는 람다식으로 객체 선언 없이 메서드만 가져와서 사용이 가능하다.
람다식에서 입력값을 그대로 사용하게 된다면 메서드 레퍼런스를 사용해 더욱 코드를 짧게할 수 있다.
익명 객체/메서드 클래스 또는 메서드를 여러번 한 번만 사용할 경우 클래스/메서드의 이름을 지정하지 않고 구현만해 단발성으로 사용한다.
익명 객체/메서드는 호출할 수 없다.
주로 안드로이드에서 쓰이며 UI, thread 작업에 사용한다.
제너릭은 특정 타입으로 지정하지 않고 외부 또는 사용자가 지정해서 사용할 수 있도록 도와준다.
제너릭은 reference타입만 지정이 가능하다.