출처 | 인프런 즐거운 자바
ex)
https://gmlwjd9405.github.io/2018/07/13/template-method-pattern.html
결과
str1==str2는 같은 곳을 참조하느냐? -> yes
str3 == str4는 같은 곳을 참조하느냐? -> no
인터페이스이름은 Upper CamelCase로 작성된다.
interface도 확장자가 .java 파일로 작성한다.
인터페이스의 모든 필드는 public static final이어야 하며, 모든 메소드는 public abstract이어야한다.(JAVA 7까지는) final, abstract를 생략하면 자동으로 붙는다.
java8 부터는 디폴드 메서드와 정적 메서드도 선언이 가능하다.
LottoMachine
// 1~45까지 써있는 Ball를 로또 기계에 넣는다.
// 로또 기계에 있는 Ball들을 섞는다.
// 섞인 Ball중에서 6개를 꺼낸다.
public interface LottoMachine {
int MAX_BALL_COUNT = 45;
// 해당 interface 변수들은 모두 public static하다. 즉, 메모리에 올라가있다.
int RETURN_BALL_COUNT = 6;
public abstract void setBalls(Ball[] balls); //배열이라고 하는것은 여러개를 의미한다. 45개를 받는다.
// Ball[] Ball 여러개를 받겠다라는의미
public abstract void mix(); // 자기가 가지고 있는 Ball들을 섞는다.
public abstract Ball[] getBalls(); // 6개의 Ball를 반환한다.
}
//인터페이스가 가지고 있는 것들은 추상메소드다.
//즉, interface는 껍데기이다. 프로그램을 만들기전에 이런 기능을 가지고 있어야 될 것 같아 라고 정의만 하는 것
//장점 : 메소드 구현을 안 해도 Todolist처럼 이용가능하다.
LottoMachineImpl
public class LottoMachineImpl implements LottoMachine{
private Ball[] balls;
@Override
public void setBalls(Ball[] balls) {
this.balls = balls;
}
@Override
public void mix() {
for(int i = 0; i < 10000; i++)
{
int x1 = (int)Math.random() * 45;
int x2 = (int)Math.random() * 45;
if(x1 != x2)
{
Ball tmp = balls[x1]; // 값을 치환할 떄는 type의 임시변수가 필요하다. swap
balls[x1] = balls[x2];
balls[x2] = tmp;
}
}
}
@Override
public Ball[] getBalls() {
Ball[] result = new Ball[6]; //6개를 참조할 수 있는 배열
for(int i = 0; i<6; i++)
{
result[i] = balls[i];
}
return result;
}
}
//인터페이스를 구현하게 되면 반드시 인터페이스가 가지고 있는 메소드를 오버라이딩할 필요가 있다.
//위에 메소드를 생성시킨 것은 로또머신이 가지고 있는 3가지 메서드가 구현된다.
Ball
public class Ball {
// 불변 객체
public int getNumber() {
return number;
}
private int number; //ball은 값을 가져야한다. ball 값 자체가 변하면 안 된다.
public Ball(int number)
{
this.number = number;
}
}
LottoMachineMain
public class LottoMachineMain {
public static void main(String[] args)
{
//Ball b1 =new Ball(1);
// ...
//Ball b45 =new Ball(45);
//변수가 45개 필요한 상황
Ball[] balls = new Ball[45]; // 인스턴스를 생성한게 아니라 45개를 참조할수있는 변수를 45개를 만든것이다.
//balls[0] = new Ball(1);
//balls[1] = new Ball(2);
//...
//balls[44] = new Ball(45); 이런식으로 들어간다.
for(int i = 0; i < 45; i++)
{
balls[i] = new Ball(i + 1);
}
LottoMachine lottoMachine = new LottoMachineImpl();
// 이 코드가 실행됐다는 것은 로또머신이 생성된것이다.
lottoMachine.setBalls(balls);
lottoMachine.mix();
Ball[] result = lottoMachine.getBalls();
for(int i = 0; i < result.length; i++)
{
System.out.println(result[i].getNumber());
}
}
}
배열 설명
인터페이스 이름은 Upper CamelCase로 작성한다.
interface도 확장자가 .java 파일로 작성한다.
인터페이스의 모든 필드는 public static final이어야 하며, 모든 메소드는 public abstract이어야 한다. final, abstract를 생략하면 자동으로 붙는다.
java8 부터는 디폴트, 메서드와 정적 메서드도 선언이 가능하다.
A라는 사용자가 메소드가 3개 선언된 interface를 작성한 후 외부에 공개를 하였다.(라이브러리로 제공)
여러 사용자들이 해당 인터페이스를 이용해 구현을 하였다.
A라는 사용자는 인터페이스에 1개의 메소드가 추가로 더 있는게 좋다고 판단하였다. 그래서 메소드를 추가 하였다.
여러 사용자들은 라이브러리가 업데이트 된 줄 알고 업데이트 하였다. 무슨 일이 벌어졌을까? -> 에러 폭탄
인터페이스의 static method(JDK 8에 추가된 메소드)
package FactoryMethod;
public class BeanFactory {
// 2. 자기 자신 인스턴스를 참조하는 static한 필드를 선언한다.
private static BeanFactory instance = new BeanFactory();
// 1. private 생성자를 만든다. 외부에서 인스턴스를 생성하지 못한다.
private BeanFactory()
{
}
// 3. 2번에서 생성한 인스턴스를 반환하는 static한 메소드를 만든다.
public static BeanFactory getInstance()
{
return instance;
}
public Bus getBus()
{
return new Bus(); //
}
}
package FactoryMethod;
public class BeanFactoryMain {
public static void main(String[] args){
BeanFactory b1 = BeanFactory.getInstance();
BeanFactory b2 = BeanFactory.getInstance();
if(b1 == b2)
{
System.out.println("b1 == b2");
}
Bus bf1 = b1.getBus(); //객체생성을 대신해주는걸 팩토리라고 한다.
Bus bf2 = b1.getBus();
}
}
package FactoryMethod;
public class Bus extends Car{
public void a()
{
System.out.println("a");
}
public void b()
{
System.out.println("b");
}
public void c()
{
System.out.println("c");
}
}
package FactoryMethod;
public class SuperCar extends Car {
public void a()
{
System.out.println("Super Car a!!!");
}
}
상황
1. a() 메소드를 가지고 있는 클래스가 있다.
2. 이 클래스 이름이 아직 무엇인지 모른다.
3. 나중에 이 클래스 이름을 알려주겠다. 라는 상황
4. a() 메소드를 실행할 수 있도록 코드를 작성해라
package FactoryMethod;
import java.lang.reflect.Method;
public class ClassLoaderMain {
public static void main(String[] args) throws Exception
{
//Bus b1 = new Bus();
//b1.a(); // a 출력 -> b1를 모르는 상황 이 때 클래스로더를 사용
String className = "FactoryMethod.Bus"; // Bus ==>SuperCar ==> Car사용 가능
Class clazz = Class.forName(className); //클래스는 ClassPath에서 찾는다.
//className에 해당하는 이름을 ClassPath에서 찾는다. 그리고 그 클래스 정보를 clazz에 잠조하도록 넣는다.
Object o = clazz.newInstance(); //clazz의 정보를 가지고 인스턴스를 만들어라 라는 뜻
//Object o = new Bus 와 동일하다.
Car b = (Car)o;
b.a(); // a 가 나온다.
//Car가 부모이기 때문에 FactoryMethod.SuperCar가 Bus건 Car건 상관이 없다.
Method m = clazz.getDeclaredMethod("a", null);
// a() 메소드 정보를 가지고 있는 Method를 반환하라.
m.invoke(o, null); // Object o가 참조하는 객체의 m 메소드를 실행해라.
// // ----version1-------------------------------------------
// Method[ ] declaredMethods = clazz.getDeclaredMethods();
// //이 clazz가 가지고 있는 정보들을 Method[ ] declaredMethods 여러개로 리턴해준다.
// for(Method m : declaredMethods) // 그 정보를 하나씩 꺼내서 배열의 정보를 하나씩 출력하면 a b c가 나온다.
// {
// /System.out.println(m.getName());
// }
}
}
package FactoryMethod;
public class CarExam {
public static void main(String[] args)
{
Car c1 = new Car(); // Car가 추상클래스여서 안돼 -> Car c1 = new Bus() 는 가능 (상속받고 있어서){
@Override
public void a() {
System.out.println("asdasdasdas");
}
c1.a();
}
}
}