메소드 안에서 다음과 같이 쌩뚱맞은 중괄호 블락이 보인다면,

이는 변수의 유효 범위(scope)를 제한하기 위함이다.

 

1
2
3
4
5
6
7
8
9
public void methodA(){
    {
        int a = 0;
    }
    
    int a = 2;
 
    return b;
}
cs

3번째 라인의 a 변수는 { } 안에서만 유효한 지역변수이며,

위와 같이 a 변수를 6번라인에서 선언해도 문제 되지 않는다.

 

* 변수 유효범위를 제한하여 코딩할 때 사용하나, 드물게 사용된다. 

 

반응형

자바 8부터 람다와 함께 사용되는 스트림.

아직 자바 8 환경에서 개발해 볼 경험이 없었지만 정리를 미리 해둔다.

 

스트림은 아래와 같은 파이프라인으로 작성한다.

객체집합.스트림생성().중개연산().최종연산() 

1. 스트림 생성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private static void construct() {
  //1. Collection으로 스트림 생성
  //collection.stream();
  List<String> names = Arrays.asList("pyo""sue");
  names.stream();
  
  //2. 배열로 스트림 생성
  //array.stream();
  int[] arr = {123};
  Arrays.stream(arr);  
  
  //3. 스트림 직접 생성
  //Stream.of(~);
  Stream<Integer> stream = Stream.of(1,2,3);   
}
cs

 

2. 중개연산

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
private static List<String> names = Arrays.asList("pyo""sue""aron""ballack""pyo");
 
//2. 중개연산
private static void middle() {
  //2-1. Filter : 특정 조건에 부합하는 데이터만 뽑을 때 사용
  long cnt = names.stream().filter(new Predicate<String>() {
     @Override
     public boolean test(String t) {
        return t.contains("y");
     }
  }).count();
  System.out.print("result of counting after filtering : " + cnt);
  System.out.println("\n-----------------------------");
  
  //2-2. Map : 특정 연산을 할 때 사용
  names.stream().map(new Function<StringString>() {
     @Override
     public String apply(String t) {
        return t.concat("@gmail.com");
     }
  }).forEach(x -> System.out.print(x+" "));
  System.out.println("\n-----------------------------");
  
  //2-3. sort : 정렬
  names.stream().sorted((x1, x2)->x1.compareTo(x2)).forEach(x->System.out.print(x+" "));
  System.out.println("\n-----------------------------");
  
  //2-4. limit : 출력 수 제한
  names.stream().limit(2).forEach(x->System.out.print(x+" "));
  System.out.println("\n-----------------------------");
  
  //2-5. distinct : 중복 제거
  names.stream().distinct().forEach(x->System.out.print(x+" "));
  System.out.println("\n-----------------------------");
  
}
 
cs

 

결과

result of counting after filtering : 2
-----------------------------
pyo@gmail.com sue@gmail.com aron@gmail.com ballack@gmail.com pyo@gmail.com
-----------------------------
aron ballack pyo pyo sue
-----------------------------
pyo sue
-----------------------------
pyo sue aron ballack
-----------------------------

3. 최종연산

1
2
3
4
5
6
7
8
private static List<String> names = Arrays.asList("pyo""sue""aron""ballack""pyo");
 
//3. 최종연산
private static void finalOperate() {
  names.stream().forEach(x->System.out.print(x+" "));
  names.stream().count();
  //names.stream().max();
}
cs

 

 

람다보단 개념이 단순해 보이나

적응되는데 시간이 필요할 듯 하다.

 

참고

반응형

 

 

[ Error vs Exception ]

Error: An Error indicates serious problem that a reasonable application should not try to catch.

> 애플리케이션이 잡지 말아야 할 심각한 에러(핸들링이 어려운 에러)

 

Exception: Exception indicates conditions that a reasonable application might try to catch.

> 애플리케이션이 잡을 수 있는 에러(핸들링이 할 수 있는 에러)

 

 

[ ClassNotFoundError vs NoDefClassFoundError ]

ClassNotFoundException is an exception that occurs when you try to load a class at run time using Class.forName() or loadClass() methods and mentioned classes are not found in the classpath.

> 런타임에 Class.forName() 혹은 loadClass() 메소드를 사용했고 refrection에 사용된 class가 classpath에 없는 경우 발생

 

NoClassDefFoundError is an error that occurs when a particular class is present at compile time, but was missing at run time.

> 특정 클래스가 컴파일시엔 존재했으나 런타임 때 찾을 수 없는 경우

위와 관련된 경험: https://developyo.tistory.com/241?category=747217

 

 

[Checked Exception vs Unchecked Exception ]

Checked Exception : Java forces you to handle these error scenarios in some manner in your application code. 

> java 가 핸들링을 강요하는 exception (ex: sqlException, IOException)

 

Unchecked Exception : A method is not forced by compiler to declare the unchecked exceptions thrown by its implementation. Generally, such methods almost always do not declare them, as well.

Spring Transaction only supervise Unchecked Exceptions.

> 핸들링을 강요하지 않는 exception (ex: NullPointerException)

 

 

참고)

error vs exception

https://www.geeksforgeeks.org/exceptions-in-java/

ClassNotFoundError vs NoDefClassFoundError

https://dzone.com/articles/java-classnotfoundexception-vs-noclassdeffounderro

CheckedException vs Unchecked Exception

https://www.geeksforgeeks.org/checked-vs-unchecked-exceptions-in-java/

 

반응형

 

예제를 위한 클래스

1. ExampleSuperClass : 부모클래스

1
2
3
4
5
package Java.reflection;
 
public class ExampleSuperClass {
}
 
cs

 

2. ExampleClass : 예제 클래스

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package Java.reflection;
 
public class ExampleClass extends ExampleSuperClass {
   
   private static final String CONSTANT_FIELD = "constant_field";
   public int i = 1;
   public String publicField = "publicStrField";
   private String privateField = "privateStrField";
   
   public ExampleClass() { 
   }
   
   public ExampleClass(Object a) throws NullPointerException {
   }
   
   private int privateMethod(Object a, int b) throws NullPointerException {
      if(a == null) {
         throw new NullPointerException();
      }
      return b;
   }
   
   public String addStrInt(String a, int b) {
      return a+b;
   }
}
cs

 

3. ExampleChildClass : 자식 클래스

1
2
3
4
package Java.reflection;
 
public class ExampleChildClass extends ExampleClass {       
}
cs

 

 

Java Reflection 의 사용 예

1. isInstance 사용법 

instanceof 와 같은 역할을 수행

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package Java.reflection;
 
//https://gyrfalcon.tistory.com/entry/Java-Reflection
 
//instance of
public class Reflection1_instanceof {
   public static void main(String[] args) {
      
      //classA.isInstance(B);
      //B가 A의 인스턴스인지를 확인
      Class clazz = ExampleClass.class;
      
      boolean rs1 = clazz.isInstance(new ExampleSuperClass());
      System.out.println(rs1);
      boolean rs2 = clazz.isInstance(new ExampleClass());
      System.out.println(rs2);
      boolean rs3 = clazz.isInstance(new ExampleChildClass());
      System.out.println(rs3);
 
   }
}
 
cs

[결과]

false
true
true

 

2. 클래스에서 메소드 정보 가져오기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package Java.reflection;
 
import java.lang.reflect.Method;
 
//Method 정보 가져오기
public class Reflection2_FindMethod {
 
   public static void main(String[] args) throws Exception {
      
      //1. class 가져오기
      Class clazz = Class.forName("Java.reflection.ExampleClass");
      //1-1. class 가져오기(위와 같다)
      Class clazz1 = ExampleClass.class;
      
      //2. class 에서 method 가져오기
      Method methodArr[] = clazz.getDeclaredMethods();
      
      for(Method method : methodArr) {
         
         //1) 메소드명 가져오기
         System.out.println("name = " + method.getName());
         
         //2) 선언된 클래스명 가져오기
         System.out.println("class = " + method.getDeclaringClass());
         
         //3) Parameter Type 가져오기
         Class paramTypes[] = method.getParameterTypes();
         for(Class p : paramTypes) {
            System.out.println("param type : " + p);
         }
      
         //4) exception Type 가져오기
         Class exceptionTypes[] = method.getExceptionTypes();
         for(Class e : exceptionTypes) {
            System.out.println("exception type : " + e);
         }
         
         //5) return Type 가져오기
         System.out.println("return type : "+method.getReturnType());
         
         System.out.println();
      }
   }
}
 
cs

[결과]

name = privateMethod
class = class Java.reflection.ExampleClass
param type : class java.lang.Object
param type : int
exception type : class java.lang.NullPointerException
return type : int

name = addStrInt
class = class Java.reflection.ExampleClass
param type : class java.lang.String
param type : int
return type : class java.lang.String

 

3. 생성자(Constructor) 정보 찾기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package Java.reflection;
 
import java.lang.reflect.Constructor;
 
//Constructor 정보 가져오기
public class Reflection3_FindConstructor {
     
   public static void main(String[] args) throws Exception {
      
      //1. class 가져오기
      Class clazz = ExampleClass.class;
      
      //2. class 에서 Constructor 가져오기
      Constructor constructors[] = clazz.getDeclaredConstructors();
      
      for(Constructor c : constructors) {
         
         //1) 생성자명 가져오기
         System.out.println("name : " + c.getName());
         
         //2) 선언된 클래스명 가져오기
         System.out.println("class : " + c.getDeclaringClass());
         
         //3) parameter type 가져오기
         Class paramTypes[] = c.getParameterTypes();
         for(Class p : paramTypes) {
            System.out.println("param type : " + p);
         }
      
         //4) exception Type 가져오기
         Class exceptionTypes[] = c.getExceptionTypes();
         for(Class e : exceptionTypes) {
            System.out.println("exception type : " + e);
         }
         
         //5) 생성자에 return type은 존재하지 않으므로 getReturnType() 메소드는 없음 
         System.out.println();
      }
   }
}
 
cs

[결과]

name : Java.reflection.ExampleClass
class : class Java.reflection.ExampleClass

name : Java.reflection.ExampleClass
class : class Java.reflection.ExampleClass
param type : class java.lang.Object
exception type : class java.lang.NullPointerException

 

4. 필드 정보 찾기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package Java.reflection;
 
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
 
//Field 정보 가져오기
public class Reflection4_FindFields {
   
   public static void main(String[] args) throws Exception {
      
      //1. class 가져오기
      Class clazz = ExampleClass.class;
      
      //2. class 에서 Field 가져오기
      //Field fields[] = clazz.getFields();        //public modifier(접근제한자) field 만 가져올 수 있다
      Field fields[] = clazz.getDeclaredFields();  //private modifier field 도 가져올 수 있다
      
      for(Field f : fields) {
         
         //1) 필드명 가져오기
         System.out.println("name : " + f.getName());
         
         //2) 필드가 선언된 클래스명 가져오기
         System.out.println("class : " + f.getDeclaringClass());
         
         //3) 필드 타입 가져오기
         System.out.println("type : " + f.getType());
         
         //4) 필드의 접근제한자 가져오기 (int 형이며 Modifier.toString(mod) 로 string으로 바꿔줄 수 있음)
         int modifiers = f.getModifiers();
         System.out.println("modifiers int : " + modifiers);
         System.out.println("modifiers toString : " + Modifier.toString(modifiers));
         System.out.println("isPublic? : " + (modifiers == Modifier.PUBLIC));
         
         System.out.println();
      }
   }
}
 
cs

[결과]

name : CONSTANT_FIELD
class : class Java.reflection.ExampleClass
type : class java.lang.String
modifiers int : 26
modifiers toString : private static final
isPublic? : false

name : i
class : class Java.reflection.ExampleClass
type : int
modifiers int : 1
modifiers toString : public
isPublic? : true

생략..

 

5. 메소드 실행시키기 (invoke)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package Java.reflection;
 
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
 
//https://kaspyx.tistory.com/80
 
//5. 메소드 실행시키기 invoke
public class Reflection5_invoke_1 {
   
   public Reflection5_invoke_1() {}
   
   public Reflection5_invoke_1(String a) {}
   
   public Reflection5_invoke_1(String a, Object b) {}
   
   public String addStrInt(String a, int b) {
      return a+b;
   }
   
   @SuppressWarnings({ "unchecked""rawtypes" })
   public static void main(String[] args) throws Exception {
      
      //1. class 가져오기
      Class clazz = Reflection5_invoke_1.class;
      
      Class paramTypes[] = new Class[2];
      paramTypes[0= String.class;
      paramTypes[1= Integer.TYPE;
      Method method = clazz.getMethod("addStrInt", paramTypes);   //* private modifier method 는 찾지 못한다
 
      Reflection5_invoke_1 methodObj = new Reflection5_invoke_1();
      Object argumentList[] = new Object[2]; 
      argumentList[0= "Str ";
      argumentList[1= new Integer(10);
      
      //java 문법은 보통 아래와 같이 S V O 
      //String rs = addStrInt("a", 10);
      //하지만, reflection 은 V.invoke(S, O)
      Object rs = method.invoke(methodObj, argumentList);
      
      System.out.println(rs);
      
   }
   
}
 
cs

[결과]

Str 10

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package Java.reflection;
 
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
 
//https://kaspyx.tistory.com/80
 
//5. 메소드 실행시키기 invoke
public class Reflection5_invoke_2 {
   
   public Reflection5_invoke_2() {}
   
   public Reflection5_invoke_2(String a) {}
   
   public Reflection5_invoke_2(String a, Object b) {}
   
   public String addStrInt(String a, int b) {
      return a+b;
   }
   
   @SuppressWarnings({ "unchecked""rawtypes" })
   public static void main(String[] args) throws Exception {
      
      Method m = ExampleChildClass.class.getMethod("addStrInt"new Class[] {String.class, Integer.TYPE});
      String result = (String) m.invoke(new ExampleClass(), new Object[]{"Str "new Integer(10)});
      System.out.println(result);
      
      //위와 같다
      Method m3 = ExampleChildClass.class.getMethod("addStrInt"new Class[] {String.class, Integer.TYPE});
      String result3 = (String) m3.invoke(new ExampleClass(), new Object[]{"Str "new Integer(10)});
      System.out.println(result3);
      
      try {
         Method m2 = ExampleClass.class.getMethod("addStrInt"new Class[] {String.class, Integer.TYPE});
         String result2 = (String) m2.invoke(new ExampleSuperClass(), new Object[]{"Str "new Integer(10)});
         System.out.println(result2);
      } catch (IllegalArgumentException ie) {
         System.out.println(ie.getMessage());
      }
   }
}
 
cs

[결과]

Str 10
Str 10
object is not an instance of declaring class

 

6. 인스턴스 생성하기 (newInstance)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package Java.reflection;
 
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
 
//https://kaspyx.tistory.com/80
 
//5. 메소드 실행시키기 invoke
public class Reflection6_newInstance {
   
   public Reflection6_newInstance() {}
   
   public Reflection6_newInstance(String a) {}
   
   public Reflection6_newInstance(String a, Object b) {}
   
   public String addStrInt(String a, int b) {
      return a+b;
   }
   
   @SuppressWarnings({ "unchecked""rawtypes" })
   public static void main(String[] args) throws Exception {
      
      Class clazz = Reflection6_newInstance.class;
      
      Class arguTypes[] = new Class[2];
      arguTypes[0= String.class;
      arguTypes[1= Integer.TYPE;
 
      Object params[] = new Object[2];
      params[0= "Str ";
      params[1= 10;
      
      Constructor myConstructor1 = clazz.getConstructor();     
      Object myObj = myConstructor1.newInstance();             
      Method m1 = clazz.getMethod("addStrInt", arguTypes); 
      String result1 = (String) m1.invoke(myObj, params);   
      System.out.println(result1);
      
      Constructor myConstructor2 = clazz.getConstructor(String.class);
      Object myObj2 = myConstructor2.newInstance("");
      Method m2 = clazz.getMethod("addStrInt", arguTypes);
      String result2 = (String) m2.invoke(myObj2, params);
      System.out.println(result2);
      
      Constructor myConstructor3 = clazz.getConstructor(String.class, Object.class);
      Object myObj3 = myConstructor3.newInstance(""new Object());
      Method m3 = clazz.getMethod("addStrInt", arguTypes);
      String result3 = (String) m3.invoke(myObj3, params);
      System.out.println(result3);
      
      Constructor myConstructor4 = clazz.getConstructor(new Class[]{String.class, Object.class});
      Object myObj4 = myConstructor4.newInstance(""new Object());
      Method m4 = clazz.getMethod("addStrInt"new Class[] {String.class, Integer.TYPE});
      String result4 = (String) m4.invoke(myObj4, new Object[] {"String " , 11});
      System.out.println(result4);
      
   }
}
 
cs

[결과]

Str 10
Str 10
Str 10
String 11

 

위의 모든 샘플 예제들은 github(https://github.com/develo-pyo)에 올려놓았습니다.

 

참고:

https://www.geeksforgeeks.org/reflection-in-java/

https://gyrfalcon.tistory.com/entry/Java-Reflection

 

반응형

jdk 1.8 이상부터 지원.

함수형 프로그래밍을 가능케 함.

1회용 익명 클래스가 필요할 때 람다를 사용하여 코드를 줄일 수 있음.

 

[기본 문법 1 : 인수가 없는 경우]

접근제한자, 리턴타입 생략 가능

public void method(){
}

() -> {
}

* @FunctionalInterface : 추상메소드가 한개만 존재하는 인터페이스

* lambda는 Functional Interface 에만 사용이 가능

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package Java.Lamda.basic;
 
public class Main1 {
        
    public static void main(String[] args) {
 
        //기존방법대로 익명클래스를 사용한 구현
        Interface i = new Interface() {
            @Override
            public void sayHello() {
                System.out.println("HELLO! in anonymous class");
            }
        };
        i.sayHello();
        
        //lambda를 사용한 익명클래스 구현        
        //lambda 는 Functional Interface 에만 사용이 가능
        Interface lamda = () -> {
            System.out.println("HELLO! in anonymous method(lambda)");
        };
        lamda.sayHello();
    }
    
    //@FunctionalInterface 는 abstract method 가 오직 1개여야 한다.
    @FunctionalInterface
    interface Interface {
        void sayHello();
    }
}
 
cs

[결과]

HELLO! in anonymous class 
HELLO! in anonymous method(lambda)

 

[기본 문법 2 : 인수가 존재하는 경우]

인수 타입 생략이 가능.

return 생략 가능(함수 내 처리가 return 문이 전부인 경우)

public void method(int x, int y){
   return x+y;
}

(x, y) -> x+y;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package Java.Lamda.basic;
 
public class Main3 {
    
    public static void main(String[] args) {
        
        Calculation add = (x, y) -> {
            return x+y;
        };
        System.out.println(calculate(add23));
        
        
        Calculation multiply = (x, y) -> x*y;
        System.out.println(calculate(multiply, 23));
    }
    
    static Integer calculate(Calculation operation, Integer x, Integer y) {
        return operation.apply(x, y);
    }
    
    @FunctionalInterface
    interface Calculation {
        Integer apply(Integer x, Integer y);
    }
    
    class Calculator {
        Integer add(Calculation cal, Integer x, Integer y) {
            return cal.apply(x, y);
        }
    }
}

 

 

cs

[결과]

5
6

 

[기본 문법 3 : static 메소드, default 메소드]

jdk 1.8 부터 interface 에서 static 메소드, default 메소드 사용이 가능.

static 메소드 : 오버라이딩 불가
default 메소드 : 오버라이딩 가능

인터페이스에 메소드라니?!

추상메소드만 가질 수 있는게 아니라니?!

인터페이스가 메소드(default 메소드)를 가질 수 있게 되면서, 추상클래스와 인터페이스가 매우 비슷해졌다.

(인터페이스를 만들 때, 공통 관심사는 default 메소드로, 나머지 메소드를 abstract 메소드로 선언하면 기존의 추상클래스와 매우 비슷하다)

자바에서도 결국 다중상속이 가능하게 된 셈이다.

그렇다면 추상클래스와 인터페이스의 차이점은 이제 더이상 없는건가?

아직 아래와 같은 차이점이 존재한다.

추상클래스 : 다중상속이 불가, 인스턴스 변수를 가질 수 있다
인터페이스 : 다중구현이 가능, 인스턴스 변수를 가질 수 없다

Calculation : interface

ExtendsCalculation : Calculation interface 상속받은 interface

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package Java.Lamda.basic;
 
public class Main4 {
 
    public static void main(String[] args) {
 
        Calculation add = (x, y) -> {
            return x+y;
        };
        add.print(23);
        Calculation.print2(23);
        System.out.println(calculate(add23));
        
        
        ExtendsCalculation multiply = (x, y) -> x*y; 
        multiply.print(23);
        System.out.println(calculate(multiply, 23));
        
    }
    
    static Integer calculate(Calculation operation, Integer x, Integer y) {
        return operation.apply(x, y);
    }
    
    @FunctionalInterface
    interface Calculation {
        Integer apply(Integer x, Integer y);
        
        default void print(Integer x, Integer y) {
            System.out.println("x : " + x);
            System.out.println("y : " + y);
        }
        
        static void print2(Integer x, Integer y) {
            System.out.println("x : " + x + ", y : " + y);
        }
    }
    
    class Calculator {
        Integer add(Calculation cal, Integer x, Integer y) {
            return cal.apply(x, y);
        }
    }
    
    @FunctionalInterface
    interface ExtendsCalculation extends Calculation{
        @Override
        default void print(Integer x, Integer y){
            System.out.println("x,y : " + x +","+ y);
        }
    }
    
}
 
cs

[결과]

x : 2
y : 3
x : 2, y : 3
5
x,y : 2,3
6

※ stackoverflow 및 각종 개발자 커뮤니티 QnA 에 Comparator Interface 구현시 람다를 어떻게 사용할 수 있냐는 질문이 꽤 많이 보인다.

1.8 에서의 Comparator 인터페이스를 들여다 보면, default 메소드 및 static 메소드 다수 존재하나 추상메소드는 오직 compare()메소드, 단 한 개 존재한다.

 

[메소드 참조]

https://developyo.tistory.com/193?category=688588

 

 

참고:

https://futurecreator.github.io/2018/07/20/java-lambda-type-inference-functional-interface/

 

반응형

'back > java' 카테고리의 다른 글

[Java] java reflection  (0) 2020.02.20
[Java] lambda 람다 2 (메소드참조)  (0) 2020.01.17
[Java] Generic 제네릭  (0) 2019.12.28
[Java] Compile  (0) 2019.12.22
[Java] Collection Framework  (0) 2019.12.21

[Generic (제네릭)]

클래스 내부에서 사용할 데이터 타입을 인스턴스 생성시 확정하는 것을 제네릭이라 한다.

제네릭은 다양한 타입의 객체를 다루는 메소드 및 컬렉션 클래스를 컴파일 시, 타입 체크를 해주는 기능을 한다.

객체 타입을 컴파일 시에 체크하기 때문에 객체의 타입 안정성을 높이고 형변환의 번거로움을 줄인다.

 

※ ArrayList 같은 컬렉션 클래스는 다양한 종류의 객체를 담을 수 있지만, 보통 아래와 같이 한 종류의 객체를 담는 경우가 더 많다. 또한 꺼낼 때 마다 타입체크를 하고 형변환 하는 것은 아무래도 불편할 수 밖에 없는데, 제네릭이 이와 같은 불편함 들을 해소해준다.

List<Person> list = new ArrayList<>();
list.add(new Person("영심이"));
list.add(new Person("홍길동"));
list.get(0);

 

1. 제네릭 타입

타입을 파라미터(<T>)로 가지는 클래스 혹은 인터페이스

타입 파라미터(<T>) 를 클래스 혹은 인터페이스 명 뒤에 두어 선언

public class Sample<T>{}
public interface Sample<T>{}

[Print.class (제네릭 타입)]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package Java.Generic;
 
public class Print<T> {
    
    private T text;
    
    public void setText(T text) {
        this.text = text;
    }
    
    public void printText() {
        System.out.println(text);
    }
}
 
cs

 

[Main.class]

p.text 는 String,

b.text 는 Integer(int) 데이터 타입을 갖게된다.

: 제네릭을 사용하여 클래스를 정의할 때 데이터 타입을 정하지 않고, 인스턴스를 생성할 때 타입을 저장

 

※ jdk 1.7 버전 이상은 실제 타입 생략 가능

Print<Integer> p = new Print<>();  // Print<Integer> p = new Print<Integer>(); 와 같음

※ Raw Type : 타입 매개변수가 없는 제네릭 클래스

generic은 jdk 1.5 버전에 지원됐으며, 1.5 버전 이전의 소스와 문제가 생기는걸 방지하기 위해 Raw Type 을 허용

아래와 같이 데이터타입을 주지 않고 선언 할 경우 데이터 타입(타입 매개변수)은 Object로 간주한다.

List list = new ArrayList<>();   //List<Object> list = new ArrayList<>(); 와 같음

[제네릭의 장점]

1. 타입안정성 제공 :

명시한 타입의 데이터가 아닌 다른 형태의 데이터 타입으로의 형변환을 방지 (위 Main.class 의 8번, 14번 참고)

2. 타입체크와 형변환 생략 가능 :

다룰 객체의 데이터 타입을 미리 명시

 

[다중 타입매개변수]

타입매개변수가 N개인 경우, 아래와 같이 사용 가능

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package Java.Generic;
 
public class Pair<K, V> {
 
    private K key;
    private V value;
    
    public Pair(K key, V value){
        this.key = key;
        this.value = value;
    }
    
    public K getKey() {
        return key;
    }
    
    public V getValue() {
        return value;
    }    
}
 
cs

Client(호출부)

Pair<String, Integer> pair1 = new Pair<>("key1", 1);
System.out.println(pair1.getKey());    //key1
System.out.println(pair1.getValue());  //1

Pair<Integer, Integer> pair2 = new Pair<>(1, 2);
System.out.println(pair2.getKey());     //1
System.out.println(pair2.getValue());   //2

[제네릭과 다형성]

public class Print{}
public class ChilePrint extends Print{}

위처럼 Print 클래스, 이를 상속하는 ChildPrint 클래스가 있을 때

아래와 같이 사용 가능.

1
2
3
4
5
6
7
8
9
10
public class Client {
    public static void main(String[] args) {
        List<Print> list = new ArrayList<>();
        list.add(new Print());
        list.add(new ChildPrint());
        
        Print p = list.get(0);
        ChildPrint cp = (ChildPrint)list.get(0);    
    }
}
cs

Print 를 상속하는 ChildPrint 는 Print 타입파라미터를 갖는 list 인스턴스에 add 할 수 있다.

 

 

2. 제네릭 메소드

리턴 타입 및 매개변수 타입을 타입 매개변수로 갖는 메소드

일반 클래스의 메소드에서도 타입 매개변수를 사용하여 제네릭 메소드 정의 가능

리턴타입 앞에 타입 매개변수(<>)를 추가한 후, 리턴타입과 매개타입을 타입 매개변수로 사용가능

 

[Cart.class]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package Java.Generic;
 
import java.util.ArrayList;
import java.util.List;
 
public class Cart<T> {
    
    private List<T> items;
    
    public Cart() {
        if(items == null)items = new ArrayList<>();
    }
    
    public void setItem(T item) {
        this.items.add(item);
    }
    
    public List<T> getItems() {
        return items;
    }
    
}
 
cs

[Calculator.class (제네릭 메소드)]

1
2
3
4
5
6
7
8
9
10
11
12
package Java.Generic;
 
public class Calculator {
    public static <T> int add(Cart<T> cart1, Cart<T> cart2) {
        int result = 0;
        
        result = cart1.getItems().size() + cart2.getItems().size();
        
        return result;
    }
}
 
cs

[Client.class (호출부)]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package Java.Generic;
 
public class Client {
    public static void main(String[] args) {
        
        Cart<String> cart1 = new Cart<>();
        cart1.setItem("초콜렛");
        cart1.setItem("슬리퍼");
        
        Cart<String> cart2 = new Cart<>();
        cart2.setItem("생수");
        
        int totalSize = Calculator.add(cart1, cart2);
        System.out.println(totalSize);  // 3
        
    }
}
cs

 

 

3. 타입 파라미터의 제한

상속 및 구현 관계를 이용하여 타입 제한

※ 상위 타입이 interface인 경우에도 extends 키워드 사용.

public <T extends 상위타입> 리턴타입 메소드(매개변수, ...) {...}

[Product.class]

최상위 클래스

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package Java.Generic;
 
public class Product {
    
    private String product;
    
    public void setProduct(String product) {
        this.product = product;
    }
    
    public String getProduct() {
        return this.product;
    }
}
 
cs

[Food.class]

Product를 상속받는 하위 클래스

1
2
3
4
package Java.Generic;
 
public class Food extends Product{}
 
cs

[Fruit.class]

Food 를 상속받는 하위 클래스

1
2
3
4
package Java.Generic;
 
public class Fruit extends Food{}
 
cs

[Cart.class]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package Java.Generic;
 
import java.util.ArrayList;
import java.util.List;
 
public class Cart<T> {
    
    private List<T> items;
    
    public Cart() {
        if(items == null)items = new ArrayList<>();
    }
    
    public void setItem(T item) {
        this.items.add(item);
    }
    
    public List<T> getItems() {
        return items;
    }
    
}
 
cs

[Calculator.class (제네릭 메소드)]

타입 파라미터를 Food 로 제한한다. (Food 혹은 Foor 하위 클래스만 타입 파라미터로 받을 수 있다)

1
2
3
4
5
6
7
8
9
10
11
12
package Java.Generic;
 
public class Calculator {
    public static <extends Food> int add(Cart<T> cart1, Cart<T> cart2) {
        int result = 0;
        
        result = cart1.getItems().size() + cart2.getItems().size();
        
        return result;
    }
}
 
cs

[Client.class (호출부)]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package Java.Generic;
 
public class Client {
    public static void main(String[] args) {
        
        Cart<Food> cart1 = new Cart<>();
        Product p = new Food();
        p.setProduct("초콜릿");
        cart1.setItem((Food)p);
        
        Cart<Food> cart2 = new Cart<>();
        Product p2 = new Food();
        p2.setProduct("과자");
       cart2.setItem((Food)p2);
        
        int totalSize = Calculator.add(cart1, cart2);
        System.out.println(totalSize);
    }
}
cs

※ 해당 메소드 호출 시, 타입 파라미터를 섞어서 사용 할 수 없다.

: 16번 라인의 .add(...) 호출 시, 넘겨주는 매개변수 cart1 의 타입 파라미터(6번 라인의 Cart<Food>)에 의해 .add(...) 메소드의 타입파라미터(Calculator.class의 4번 라인의 <T>)가 정해진다.

 

예를 들어, 아래와 같은 경우

14번 라인에서 빨간줄이 그어진다. 

cart3 의 Fruit 으로 .add(...) 메소드의 타입파라미터가 정해지지만, Food 를 담고있는 cart4 를 매개변수로 넣으려고 하고 있으므로 not applicable for the arguments 에러가 발생한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package Java.Generic;
 
public class Client {
    public static void main(String[] args) {
        
        Cart<Food> cart1 = new Cart<>();
        Calculator.add(cart1, cart1);
        
        Cart<Fruit> cart2 = new Cart<>();
        Calculator.add(cart2, cart2);
                
        Cart<Fruit> cart3 = new Cart<>();
        Cart<Food> cart4 = new Cart<>();
        Calculator.add(cart3, cart4);    //compile error
    }
}
cs

 

 

4. 와일드카드

1. 제네릭 타입<?>: Unbounded Wildcards(제한 없음)

  타입 파라미터로 모든 클래스나 인터페이스 타입이 올 수 있다.

2. 제네릭 타입<? extends 상위 타입>: Upper Bounded Wildcards(상위 클래스 제한)

  타입 파라미터로 상위 타입의 하위 타입만 올 수 있다.

3. 제네릭 타입<? super 하위 타입>: Lower Bounded Wildcards(하위 클래스 제한)

  타입 파라미터로 하위 타입의 상위타입만 올 수 있다.

 

[example]

public class Product {}
public class Food extends Product {}
public class Fruit extends Food {}

[Calculator.class : 타입 파라미터 제한과 와일드카드 사용의 차이]

add1(...) : 타입 파라미터를 제한

add3(...) : 와일드카드를 사용하여 매개변수의 타입을 제한

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package Java.Generic;
 
public class Calculator {
    public static <extends Food> int add1(Cart<T> cart1, Cart<T> cart2, Cart<T> cart3) {
        //제네릭 메소드에서의 타입 파라미터의 제한
        //타입파라미터를 Food 로 제한
        //매개변수는 타입파라미터인 T를 갖는 Cart이며, T는 Food 및 Food 의 subclass 로 제한
        int result = 0;
        result = cart1.getItems().size() + cart2.getItems().size() + cart3.getItems().size();
        return result;
    }
    
//    public static <T super Food> int add2(Cart<T> cart1, Cart<T> cart2, Cart<T> cart3) {    
//        //제네릭 메소드에서 타입 파라미터를 제한할 때 위와 같은 super키워드는 불가 
//        //compile error
//        int result = 0;
//        result = cart1.getItems().size() + cart2.getItems().size() + cart3.getItems().size();
//        return result;
//    }
    
    public static int add3(Cart<super Food> cart1, Cart<?> cart2, Cart<extends Food> cart3) {
        //매개변수의 타입을 와일드카드 (?)를 사용하여 제한
        //cart1 맥대변수(Cart)의 타입파라미터 : Food 및 Food 를 subclass 
        //cart2 매개변수(Cart)의 타입파라미터 : 아무 타입이나 담고있는 Cart
        //cart3 매개변수(Cart)의 타입파라미터 : Food 및 Food 의 subclass
        int result = 0;
        result = cart1.getItems().size() + cart2.getItems().size() + cart3.getItems().size();
        return result;
    }
}
 
cs

[Client.class]

1. add1사용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package Java.Generic;
 
public class Client {
    public static void main(String[] args) {
        
        Cart<Food> cart1 = new Cart<>();
        Calculator.add1(cart1, cart1, cart1);    //타입파라미터를 Food 로 지정
        
        Cart<Fruit> cart2 = new Cart<>();
        Calculator.add1(cart2, cart2, cart2);    //타입파라미터를 Fruit 으로 지정
        
        Cart<Fruit> cart3 = new Cart<>();
        Cart<Food> cart4 = new Cart<>();
        Calculator.add1(cart3, cart3, cart4);    //car3 매개변수에 의해 Fruit 으로 타입파라미터가 정해지지만, cart4 Food를 넣으려하기 때문에 Compile Error
        
    }
}
 
cs

2. add3 사용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package Java.Generic;
 
public class Client {
    public static void main(String[] args) {
        
        Cart<Product> cart1 = new Cart<>();
        Product p = new Product();
        p.setProduct("바가지");
        cart1.setItem(p);
        
        Cart<String> cart2 = new Cart<>();
        cart2.setItem("연필");
        
        Cart<Fruit> cart3 = new Cart<>();
        Product p2 = new Fruit();
        p2.setProduct("사과");
        Product p3 = new Fruit();
        p3.setProduct("수박");
        cart3.setItem((Fruit)p2);
        cart3.setItem((Fruit)p3);
        
        int totalSize = Calculator.add3(cart1, cart2, cart3);
        System.out.println(totalSize);
    }
}
cs

 

※ 

E - Element (used extensively by the Java Collections Framework)

K - Key

N - Number

T - Type

V - Value

 

 

참고:

https://devbox.tistory.com/entry/Java-%EC%A0%9C%EB%84%A4%EB%A6%AD

https://movefast.tistory.com/74

https://ict-nroo.tistory.com/42

 

 

 

 

반응형

'back > java' 카테고리의 다른 글

[Java] lambda 람다 2 (메소드참조)  (0) 2020.01.17
[Java] lambda 람다 1 (기본문법)  (0) 2020.01.15
[Java] Compile  (0) 2019.12.22
[Java] Collection Framework  (0) 2019.12.21
Comparator, Comparable + Arrays.sort() 그리고 인터페이스..  (0) 2019.09.02

[클라이언트 ip 가져오기]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public static String getIp(HttpServletRequest request){
    String result = null;
    
    result = request.getHeader("X-Forwarded-For");
    
    if (result == null || result.length() == 0 || "unknown".equalsIgnoreCase(result)) {
        result = request.getHeader("Proxy-Client-IP");
    }
    if (result == null || result.length() == 0 || "unknown".equalsIgnoreCase(result)) {
        result = request.getHeader("WL-Proxy-Client-IP");
    }
    if (result == null || result.length() == 0 || "unknown".equalsIgnoreCase(result)) {
        result = request.getHeader("HTTP_CLIENT_IP");
    }
    if (result == null || result.length() == 0 || "unknown".equalsIgnoreCase(result)) {
        result = request.getHeader("HTTP_X_FORWARDED_FOR");
    }
    if (result == null || result.length() == 0 || "unknown".equalsIgnoreCase(result)) {
        result = request.getRemoteAddr();
    }
    
    return result==null?"":result; 
}
 
cs

 

[서버 ip 가져오기]

1
2
3
4
5
6
7
8
9
 public static String getIp(){
    String result = null;
    try {
        result = InetAddress.getLocalHost().getHostAddress();
    } catch (UnknownHostException e) {
        result = "";
    }
   return result; 
}
cs

 

찾다보니 아래와 같은 글도 찾게 되었다.

https://pkgonan.github.io/2018/06/InetAddress-getLocalHost

 

반응형

+ Recent posts