0%

안드로이드스튜디오는 이클립스기반이 아니라 인텔리J기반 IDE 툴이다. 따라서 상당히 햇갈리는 부분이 많이 있다.
그중 하나가 자동임폴트이다.

셋팅전
안드로이드스튜디오의 초기 셋팅은 자동임폴트 기능이 enabled되어 있다. 위 이미지처럼 import 되어있지 않은 클래스를 코딩할 때 마다 Alt+Enter 를 눌러 달라고 귀찮게 한다.

안드로이드스튜디오에서 자동임폴트 기능을 활성화 하면 위 같은 귀찮은 것들을 줄일 수 있다.

셋팅화면
안드로이드스튜디오의 좌측 상단의 file - Setting 화면에서 Editor - General - Auto Import 화면에서 Optimize imports on the fly, Addunambiguos imports on the fly 두개 항목을 체크하면 된다.

셋팅이 완료되면 타이핑 하는 그 즉시 자동으로 import가 되며, 번거롭게 Alt+Enter 를 누를 필요가 없어진다.

안드로이드스튜디오에서 개발중인 앱을 가상에뮬레이터에 동작시킬 때 간혹 Error running app: instant run requires ‘tools android enable adb integration’ to be enabled 라는 메세지와 함께 동작을 안하는 경우가 있다.
오류메세지 말 그대로 enable adb integration 라는 Android tools 가 enabled(비활성화) 되었다는 것이다. 이것을 활성화 해주면 문제가 해결된다.
잘 쓰다가 갑작이 왜 비활성화가 되었는지는 아직 의문이다.

오류내용

일단 안드로이드스튜디오의 상단 메뉴 Tools -> Android -> Enable ADB Integration 항목을 확인해 보면 비활성화 된 것은 확인 할수있다.

셋팅전

해당 항목을 클릭하여 활성화 시키면, 정상적으로 안드로이드앱이 가상에뮬레이터에서 동작 한다.

셋팅후

중첩인터페이스(Nested Interface)는 사실 중첩클래스(Nested Class)와 같은 맥락을 갖는다.
중첩클래스가 클래스 내부에 선언된 클래스라면 중첩인터페이스는 클래스 내부에 선언된 인터페이스이다.
특정 클래스와 아주 긴밀하 연관된 인터페이스일 경우 중첩인터페이스를 사용하며, UI프로그래밍에서 이벤트 처리용으로 주로 사용된다.

중첩클래스의 경우 인스턴스멤버클래스, 정적멤버클래스, 로컬클래스 3가지의 선언 방법이 있지만, 중첩인터페이스는 클래스의 멤버로만 선언 할 수 있다.

형태

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class ButtonA{

OnClickListener listener;

void setOnClickListener(OnClickListener listener){
this.listener = listener
}

void touch(){
this.listener.onClick();
}

/* 내부인터페이스 */
interface OnClickListener{
void onClick();
}
}

ButtonA클래스는 listener를 소유하고 사용한다.(has-a)
만약 ButtonA, ButtonB, ButtonC클래스가 존재하며 각기 다른 타입의 listener를 사용할 경우 즉 자신만이 사용하는 타입의 인터페이스를 선언할 경우 외부가 아니라, 위 처럼 자신 내부에 중첩인터페이스로 선언하여 인터페이스의 외부적변경을 경계할 수 있다.

1
2
3
4
5
6
7
//내부인터페이스 구현 클래스
public class ListenerA implements ButtonA.OnClickListener{
@Override
public void onClick(){
System.out.println("난 ButtonA를 위해서만 일한다.");
}
}

위처럼 내부 인터페이스를 구현 할 수 있다.

중첩클래스

중첩클래스란(Nested Class)?

클래스 내부에 선언한 클래스를 말한다. 인터넷을 찾아보니, 중첩클래스(Nested Class)와 내부클래스(Inner Class) 이름을 혼용해서 사용 하는듯 하다. 본 글에서는 중첩클래스(Nested Class)로 표기한다.
중첩클래스는 크게 중첩클래스와 중첩인터페이스 크게 두개로 나뉜다. 이번글은 중접클래스만 다루도록 하겠다.
중첩클래스(Nested Class)를 사용하면 두 클래스의 멤버들을 서로 쉽게 접근할 수 있다는 장점과 외부에서 불필요한 관계 클래스를 감춤으로써 코드의 복잡성을 줄일 수 있다.

중첩클래스(Nested Class)의 활용에 대한 개인적 고민

처음 중첩클래스(Nested Class)를 접했을 때, 대체 이것을 어디에 써먹을 수 있을까 고민을 했었다.
당시 고민을 하다가 애라 모르겠다 하고 그냥 책의 페이지를 넘긴 기억이 난다. 그 고민을 지금 다시 정리해 본다. (개인적인 생각 정리라 참고만 하길…)

자바라는 언어는 객체지향 언어이고 클래스 단위로 프로그래밍을 해야 한다. 내가 만약 어떤 프로그래밍 소스가 필요하다면 그것을 클래스로 만들고 그걸 구현해야 한다.

로직 -> 클래스 선언(클래스 파일생성) -> 클래스를 객체화

자바에서는 소스코드의 단위를 클래스 단위로 강제하고 있다. 이로 인해 클래스의 다양한 장점을 이용해서 코딩을 할 수있다. 다들 들어본 재사용성, 다형성, 캡슐화등이 그 대표적인 것들이다.
하지만 만약 재사용이 필요하지 않고 일회성 소스코드를 만들어 사용할 경우에 그 것들을 클래스 파일로 만들어서 사용해야 할까? 재사용이 필요하지 않는 클래스파일의 생성은 소스코드관리의 관점에서 좋지 못한 선택이다.

중첩클래스(Nested Class)는 이런 문제를 해소하기 위해 사용되는 것이라고 생각된다.
물론 익명클래스도(anonymouse class) 같은 고민을 해결해 준다. 중첩클래스(Nested Class)와 익명클래스는 둘 다 일회성 코드를 만드는 방법이지만 재사용성 노출범위에서, 그 정도에 차이가 있어보인다.
중첩클래스는 클래스를 파일 단위로 만들지는 않지만, 협소하게 재사용이 가능하게 클래스 선언을 한다. 따라서 그만큼 더 클래스의 장점을 사용 할 수 있다. 그에 반해 익명클래스는 클래스를 선언조차 하지 않는다.

중첩클래스와 익명클래스는 코딩시에 상황과 필요에 따라 선택되어 사용될 뿐이다.

참고로 디폴트클래스(defualt class)도 하나의 방법이 되겠지만, 디폴트클래스(defualt class)는 프로그래밍의 구조를 너무 느슨하게 만드는 단점을 가지기 때문에 개인적으로 지양한다. 따라서 굳이 언급하지 않았다.

중첩클래스의 종류

인스턴스 멤버클래스

1
2
3
class A{
class B{ ... }
}

특징 : 외부 클래스 A의 인스턴스 영역에 선언된 중첩클래스를 인스턴스 멤버클래스 라고 부른다. 인스턴스 멤버클래스(B)에서는 정적필드, 정적메소드를 선언할수 없으며, 외부 A클래스의 객체를 통해 내부 중첩클래스B에 접근하여 B의 구현객체를 생성할 수 있다. B로 구현된 객체는 외부객체의 인스턴스 필드와 외부객체의 클래스의 static영역에 접근 할 수 있다.

정적 멤버클래스

1
2
3
class A{
static class B{ ... }
}

특징 : 인스턴스 멤버클래스와 다르게 static(정적)으로 선언된 정적 멤버클래스는 모든 종류의 필드, 메소드를 선언 할 수 있으며, 바깥 A클래스를 통해 내부클래스 B에 접근하여 객체를 생성 할 수 있다.
당연히 내부클래스의 객체는 외부클래스로 구현된 객체의 인스턴스 영역에는 접근 할수 없으며 외부 클래스 A의 static(정적)영역에만 접근 할 수있다.

로컬클래스

1
2
3
4
5
class A{
void method1(){
class B{ ... }
}
}

특징 : method1가 실행할 때만 B중첩클래스 사용가능 하다. 외부 클래스의 모든 영역에 접근 가능하다. 심지어 method1의 메개변수와 로컬변수에도 접근이 가능한데, method1함수가 종료된 이후에도 로컬클래스의 객체는 힙 메모리에 존재해서 계속 사용 될 수 있다.

본래 매개변수나 로컬변수는 메소드실행이 종료되면 메모리에서 제거되어야 할 대상이다. 하지만 그 매개변수와 로컬변수를 로컬클래스로 생성된 객체가 참조한 경우가 문제가 될 수 있다.
자바는 이런 겅우를 위해 컴파일 시 로컬클래스에서 사용하는 매개변수나 로컬변수의 기억장소를 로컬클래스 내부에 복사해 두고 사용한다. (자바스크립트의 클로저를 연상시킨다.)
중요한 점은 참조만 가능하다는 것이다, 로컬클래스에서 자신을 감싸고 있는 함수의 매개변수와 로컬변수를 접근해서 수정을 할 수 없다. 아니 없어야 한다. (자바스크립트와는 다르다.)
따라서 컴파일러는 로컬클레스애서 자신을 감싸고 있는 외부함수의 로컬변수, 매개변수를 수정할 수 없게 강제한다.
java7 버전에서는 로컬클래스에서 참조하는 함수의 매개변수와 로컬변수는 반드시 명시적으로 final 선언 했어야 했다.
java8에서는 자동으로 final키워드를 내부적으로 붙여준다.

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
public class Outter {
//자바7 이전
public void methodJava7(final int arg) {
final int localVariable = 1;
class Inner {
public void method() {
int result = arg + localVariable;
System.out.println("java7 로컬클래스:" + result);
}
}// Inner

Inner java7Inner = new Inner();
java7Inner.method();

}//methodJava7

//자바8 이후
public void methodJava8(int arg) {
int localVariable = 2;
class Inner {
public void method() {
int result = arg + localVariable;
System.out.println("java8 로컬클래스:" + result);
}
}// Inner

Inner java8Inner = new Inner();
java8Inner.method();
}//methodJava8

public static void main(String [] args) {
Outter o1 = new Outter();
o1.methodJava7(7);
o1.methodJava8(8);
System.out.println("----");
}
}

결과

1
2
3
java7 로컬클래스:8
java8 로컬클래스:10
----

이 중첩클래스라는 것을 잘 쓸지 안쓸지는 모르겠으나, 문법적 구조는 눈에 익혀둘 필요가 있어보여 정리해본다.

익명클래스
중첩인터페이스

java8에서 interface의 문법이 약간 변경되어 그 내용을 정리하고자 한다.
java7 까지는 인터페이스에 상수, 실행블록이 없는 추상 메소드 선언만 가능했다. 하지만 java8부터 인터페이스에 디폴트메소드와 정적메소드도 추가로 선언이 가능하다. 이로인해 java의 인터페이스는 더욱 유연해진 코딩을 할 수 있다.

java8의 인터페이스 형태

1
2
3
4
5
6
7
8
9
10
interface 인터페이스명{
//상수
타입 상수명 = 값;
//추상 메소드
타입 메소드명(매개변수, ...);
//디폴트 메소드
default 타입 메소드명(매개변수, ...){ ... }
//정적 메소드
static 타입 메소드명(매개변수){ ...}
}

자바8에서 인터페이스의 디폴트메소드와 추상메소드가 추가된 이유가 있다.
어떤 프로그램에서 인터페이스 A가 있고 이 인터페이스를 구현한 A1, A2, A3…, A10 총 10개의 구현 클래스가 있다고 가정하자. 5년정도 사융하다가 인터페이스 A에 추가기능이 필요해서 추상메소드를 한개 추가하고 새로이 B1이라는 클래스로 인터페이스 A를 구현하게 된다. 문제는 기존에 사용하던 A1 ~ A10의 구현객체에서 B1클래스를 위해 추가된 추상메소드를 추가로 구현해야 한다는 점이다.
java8에서는 인터페이스의 디폴트 메소드와, 정적메소드를 추가하여 프로그래밍의 유연성을 높여주고 있다.


인터페이스의 디폴트메소드(default method)

디폴트 메소드는 인터페이스에 선언되지만 사실은 객체(구현객체)가 가지고 있는 인스턴스 메소드라고 생각해야 된다. 따라서 인터페이스를 구현한 객체를 통해서 호출 해야만 한다. 자바8에서 디폴트 메소드를 허용한 이유는 기존 인터페이스를 확장해서 새로운 기능을 추가하기 위해서이다. 만약 구현객체에서 인터페이스의 디폴트메소드가 적절하지 못하다면, 오버라이딩 하여 수정해서 사용하면 된다.

형태 : [public] default 리턴타입 메소드명(매개변수, …){ … }

인터페이스의 정적메소드(static method)

정적 메소드도 역시 자바8 부터 작성할 수 있는데, 디폴트 메소드와는 달리 객체가 없어도 인터페이스만으로 호출이 가능하다.

형태 : [public] static 리턴타입 메소드명(매개변수, …){ … }

java8의 인터페이스의 디폴트메소드, 추상메소드 예제

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
interface Java8InterfaceTest{

int talkCnt = 0;

void run();
//디폴트 메소드
default void talk(String msg){

if(msg != null && msg.equals("")){
System.out.println(msg);
}else{
System.out.println("난 디폴트 메소드다!");
}

}// talk
//정적 메소드
static void talk2(String msg){
if(msg != null && msg.equals("")){
System.out.println(msg);
}else{
System.out.println("난 정적 메소드다!");
}

}// talk2
}

public class InterfaceTest1 {

public static void main(String[] args) {

Java8InterfaceTest.talk2(null); // 정적메소드 호출

Java8InterfaceTest test1 = new Java8InterfaceTest(){

@Override
public void run() {
System.out.println("달려라!");
}

};

test1.run();
test1.talk(null); // 디폴트 메소드 호출

}

}
실행결과
1
2
3
난 정적 메소드다!
달려라!
난 디폴트 메소드다!

인터페이스의 상속에서 디폴트메소드의 변화

인터페이스는 인터페이스를 상속 할 수 있다. 자식 인터페이스에서 부모의 인터페이스를 상속할 때, 부모의 디폴트 메소드를 어떻게 할지 선택 할 수있다.

  1. 부모 인터페이스의 디폴트 메소드를 그냥 상속한다.

  2. 부모 인터페이스의 디폴트 메소드를 재정의(Override)한다.

  3. 부모 인터페이스의 디폴트 메소드를 추상메소드로 재 선언한다.

인터페이스의 디폴트메소드는 인터페이스의 강제성을 조금 유연하게 해주는 기능이다. 하지만 좋게 말하면 유연이고 나쁘게 말한다면 느슨한 것이다. 만약 이 느슨한것을 다시 엄격하게 수정할 필요가 있다면 인터페이스를 상속하여 다시 추상메소드로 재 선언 하는것도 방법이다.
개인적으로 java8의 인터페이스의 변화는 개발자의 자유도를 높여주는 기능처럼 보이며, 상당히 마음에 든다.


인터페이스 상수 선언(public static final)할 때 주의할점

java8의 특징은 아니지만, 자주 햇갈리는 부분이라 추가로 기록한다.
일반적인 클래스에서 상수(static final)의 초기화는 선언과 동시 그리고 정적영역(static block) 두 곳 모두에서 가능하다. 하지만 인터페이스의 상수는 정적영역(static block)에서 초기화 할수 없기 때문에 반드시 선언과 동시에 초기화 해야 한다.


익명구현객체(Anonymouse class)란 말 그래도 이름이 없는 객체이다.일반적으로 인터페이스를 사용하기 위해서는 해당 인터페이스를 구현한 클래스를 만들어야 한다. 예를 들어 하나의 인터페이스를 구현하는 클래스 3개를 만든다면, 3개의 java파일을 미리 만들어야 한다. 하지만 만약 재사용이 불필요한 구현클래스가 필요할땐 어떻게 해야 할까? 한번만 사용할 소스코드를 XXX.java 라는 파일로 만들고 그 것을 파일로 관리 해야만 할까? 프로그래밍에서 관리의 대상이 많아지는 것은 그 만큼 유지보수의 비용이 증가한다는 의미이다.

이렇게 일회성이며, 재사용할 필요 없고 파일단위의 관리 대상이 아닌 객체를 만들때 이 익명구현객체라는 것을 활용 할 수 있다. 문법 형태는 다음과 같다.

1
2
3
4
5
6
인터페이스명 변수 = new 인터페이스명(){
//인터페이스에 선언된 추상메소드의 구현 메소드
//해당 인터페이스에 선언된 모든 추상메소드를 구현해야 한다. 없을시 컴파일 에러 발생
//추가적으로 필드와 메소드를 선언 할 수 있지만, 익명객체 안에서만 사용할수있다.
//익명구현객체도 컴파일시 클래스파일이 만들어 지긴한다. 인터페이스명$1(순번)
}; //익명 클래스의 선언은 하나의 실행문이므로 끝에 세미콜론(;)이 무조건 붙는다.

간단한 예제

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
//구현할 인터페이스
interface Car{
public void run();
public void stop();
}

public class AnonymouseTest1 {

public static void main(String[] args) {
//익명구현 객체 fooCar
Car fooCar = new Car(){
public void run(){
System.out.println("달려라!");
}

public void stop(){
System.out.println("머.. 멈춰!!!");
}
};

fooCar.run();
fooCar.stop();
}

}

실행결과

1
2
달려라!
머.. 멈춰!!!

한번 쓰고 버릴 객체가 필요할때 이 익명구현객체란 것을 이용할 수 있다.
익명구현객체는 java8의 람다식이나 UI 프로그래밍에서 이벤트의 처리용으로 많이 사용되기 때문에 그 형태를 알아둘 필요가 있다.

Java에서 익명클래스에 대한 착각(a illusion of anonymous inner class)

override annotation
java5부터 어노테이션이라는 문법이 제공된다. 어노테이션이란 java의 소스코드를 설명하는 메타데이터다. 형태는 @어노테이션명 이다. 즉 소스코드의 특정부분(클래스, 인터페이스, 메소드, 필드 …)에 @어쩌구저쩌구를 붙여서 이 소스코드를 설명한다는 것인데… 누구에게 무엇을 설명한다는 것일까?

  1. 컴파일러에게 코드 문법 에러를 체크하도록 정보를 제공
  2. 소프트웨어 개발 툴이 빌드나 배치 시 코드를 자동으로 생성할 수 있도록 정보를 제공
  3. 실행 시(런타임 시) 특정 기능을 실행하도록 정보를제공

컴파일러

어노테이션의 설명의 대상 중 하나는 컴파일러다. 컴파일러에게 이 소스코드를 어떻게 처리해야 되는 것인지 표시를 해준다. 예를들어 내장 어노테이션인 @override경우 해당 메소드가 부모클래스를 오버라이딩 한 메소드라고 컴파일러에게 미리 일러주는 것이다. 따라서 컴파일러는 런타임 이전에 이 메소드가 문제없이 오버라이딩 되었는지 검사한다.
개발툴, 빌드툴 등도 이 컴파일러와 비슷한 맥락으로 어노테이션을 사용한다고 생각된다.


개발자

어노테이션이 만들어진 목적은 프로그래밍에서 사용하던 xml 파일들을 줄이기 위해서라고 한다. 즉 프로그램에서 사용하는 xml 형태의 다양한 설정정보들, 특히 소스코드를 설명하는 내용의 설정 파일들을 대체하기 위해서이다. 예를 들어 n개의 클래스와 그 n개의 클래스의 설정정보가 xml로 되어있다면 관리의 대상이 두배가 된다.
어노테이션은 이런 xml들을 대신 설명이 필요한 대상과 그 설명을 합치게 되었고, 그 결과 어노테이션이란 것이 만들어 졌다.

예를들어 개발자는 소스코드에 있는 @overide를 보고, 해당 메소드는 상속 받은 것 임을 확인할수 있다.


컨테이너

어노테이션의 설명의 대상 세번째는 컨테이너 이다.
스프링 프레임워크를 만져본 개발자라면 수많은 스프링 어노테이션을 본적이 있을 것이다.
프리임워크는 자신 스스로 자바의 클래스를 객체화 하고 조립하고 사용한다. 즉 자신이 주체가 되어 자바 인스턴스의 라이프사이클을 관리한다. 이때 이 어린아이와 같은 프레임워크가 가지고 놀 클래스에 어노테이션으로 라벨을 붙여, 이 부품(클래스)는 용도가 무엇이고 어떻게 사용하고, 문제가 있을시에 어떻게 해야 하며, 테스트는 어떻게 하는지 기타 등등의 정보를 알려줄수 있다.


정리

자바책에서 어노테이션을 설명하면서, 커스텀어노테이션도 설명 한다. 하지만 공부를 하면서 내가 이 어노테이션을 공부해서 어디에 활용 할 수 있지? 라는 고민이 들었다.
어노테이션은 메타데이터이다. 즉 프로그램 그 자체에서 동작하는 코드가 아니다.
만약 컴파일러, 개발툴, 빌드툴, 테스트툴 또는 스프링프레임워크와 같은 컨테이너를 개발하지 않는 이상 직접 어노테이션을 만들어서 사용해 볼 일이 거의 없을 것 같다.


java의 정적필드는 클래스로더에 의해 메소드영역에 로드가 된다.정적 필드는 객체생성 이전 클래스가 메모리에 올라가는 시점부터 사용이 가능하다. 따라서 클래스의 생성자는 정적필드의 초기화 위치로 적합하지 못하다. 그렇다면 정적필드는 어떻게 초기화 해야 할까?

정적필드 초기화 1 : 선언과 동시에 초기화 하기

1
static double pi = 3.14;

일반적으로 정적필드는 위와 같이 선언과 동시에 초기화 한다. 하지만 외부환경의 영향이나 로직을 통해 정적필드를 초기화 하고 싶을 경우도 있을 것이다.

정적필드 초기화 2 : 정적영역(static block) 사용

1
2
3
4
5
static double pi;
static{
클래스명.pi = 3.14;
System.out.println("정적영역 초기화 완료...");
}

static { … } 형태의 정적영역을 사용하여 정적필드를 초기화 할 수 있다. 정적영역 역시 클래스가 메모리에 로드되면서 동작한다.

정적영역 사용시 주의점 1 : 정적영역에서 인스턴스 멤버에 접근 할 수 없다.

정적영역은 클래스 단위의 영역이다. 모든 인스턴스는 자신의 클래스의 정적필드에 접근이 가능하지만, 그 반대로 정적영역에서 인스턴스의 필드에 접근 할 수 없다. 당연한 말일 것이다. 정적영역에서는 접근할 인스턴스를 구별할 수 없기 때문이다.

정적영역 사용시 주의점 2 : static final도 정적영역에서도 초기화 할 수 있다.

1
2
3
4
static final double PI;  //관용적으로 상수인경우 상수명을 대문자 사용
static {
클래스명.PI = 3.14;
}

java에서 일반적인 인스턴스 단위의 final 필드을 초기화 하는 방법은 필드 선언과 동시 또는 생성자에서 초기화 해야 한다.다른 방법은 존재하지 않는다.
static final 필드는 final이전에 static영역, 즉 클래스 단위의 필드이기 때문에 일반적인 static 필드 처럼 선언시와 정적영역(static block)에서 초기화 할 수 있다.

정적영역 사용시 주의점 3 : interface의 상수는 정적영역에서 초기화 할 수 없다.

인터페이스의 멤버변수는 모두 상수(public static final)이다. 하지만 이 상수는 정적영역에서 초기화 할 수 없으며, 반드시 선언과 동시에 초기화 해야 한다. (20170917 추가)

tips
java의 함수에는 일반적으로 동일한 자료형의 여러개 데이터를 인자로 받을 때, 배열을 인자로 사용 할 수 있다. 하지만 arbitrary number of arguments(… 함수인자)를 사용할 경우 함수는 배열보다도 유연하게 인자를 받을 수 있다.

1
int sum(int ... values){...};

위와 같은 자료형 … 매개변수명 형태로 함수의 인자를 선언 할 수 있다. 예제를 보면 쉽게 이해가 된다.

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
//int형의 파라미터가 몇개가 넘어올지 모를 경우, 대응 할 수 있는 ... 확장된 배열형태의 매개변수 형태
static int sum(int ... values){
int total = 0;
//배열로 인자에 접근 할 수 있다.
for(int i =0; i < values.length; i++){
total += values[i];
}// for
return total;
}// sum

public static void main(String [] args){

//인자를 아예 안받을 수도 있다.
System.out.println(sum()); // 결과: 0

//인자를 하나만 받을 수 있다.
System.out.println(sum(1)); // 결과: 1

//물론 3개도 된다.
System.out.println(sum(1, 2, 3)); // 결과: 6

//배열로도 받을 수 있다.
System.out.println(sum(new int[] {1, 2, 3, 4})); // 결과: 10

int[] values = {1,2,3,4,5};
System.out.println(sum(values)); // 결과: 15

}

함수를 선언 할때 *동일한 형태의 자료형의 인자가 0개 또는 n가 예상 되거나, 또는 배열형태의 인자가 예상될 경우 *함수 오버로딩을 대신하여 위의 arbitrary number of arguments 사용하면 편 할 것 같다.

다만 주의 할 점은 arbitrary number of arguments(… 함수인자)를 사용한 경우 해당 함수는 다른 인자는 갖을 수 없다.

1
2
3
int sum(int ... values, int b){...};  // 컴파일 오류

int sum(int ... values, String b){...}; // 컴파일 오류


이번에 기록할 내용은 this생성자 이다. (this 키워드가 아니다.)
java의 클래스 생성자도 역시 오버로딩이 가능하다. 함수 오버로딩은 훌륭한 기능이다. 다만 일반 함수의 오버로딩이 아니라 생성자의 오버로딩일 경우 객체의 멤버변수를 셋팅하는 경우가 많기 때문에 중복된 소스가 자주 발생한다.

this() 생성자란 클래스의 생성자 오버로딩시 반복되는 소스를 줄여주는 훌륭한 방법이다.
생성자 내부의 코드 블록 첫줄에 this()를 호출함으로 자신의 다른 생성자를 호출 할 수 있다. 주의할 점은 생성자 코드블록 내부의 this() 위에 다른 소스코드가 존재해서는 안된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Person {
private String name;
private int age;
private String sex;

public Person(String name){
//this.name = name; // this()생성자 위에 있는 코드에는 컴파일 오류가 발생한다.
this(name, 12); //자동으로 해당 파라미터에 적합한 자신의 생성자를 호출한다.
System.out.println("this생성자 아래에는 다른 코드가 존재 할 수 있다.");
}

public Person(String name, int age){
//this.name = name; // this()생성자 위에 있는 코드에는 컴파일 오류가 발생한다.
//this.age = age; // this()생성자 위에 있는 코드에는 컴파일 오류가 발생한다.
this(name, age, "Man"); //자동으로 해당 파라미터에 적합한 자신의 생성자를 호출한다.
System.out.println("this생성자 아래에는 다른 코드가 존재 할 수 있다.");
}

public Person(String name, int age, String sex){
this.name = name;
this.age = age;
this.sex = sex;
}
}

위 예제소스를 보면 쉽게 이해가 될것이다. 편리한 기능이라 생각되어 이렇게 기록해 놓는다.