본문 바로가기

Java/생활코딩

자바(Java) 개념 정리 - 메소드(method) #15

자바에서 method는 수학에서 함수 f(x)와 비슷한 개념이다.

 

function

method

 

복잡한 것을 정리해서 다시 단순하게 만드는 정리정돈.

public class FirstMethod {
       public static void main(String[] args) {
              
              System.out.println("Hello Method");
              System.out.println(Math.floor(1.1)); // 소수점을 내림해주는 함수
       }
}

클래스를 실행시킬 때는 main 이라는 특수한 method를 통해 출력시킨다. 자바가 first method를 실행시켜줘 라는 명령을 내리면 main이라는 method를 실행시키게 된다. 그러면 main이라는 메소드는 main 클래스의 본문 내용이 된다. 

 

System.out.println 의 경우 println은 input을 console 창에 output해주는 메소드이다.

Math.floor의 경우 floor은 input의 값을 내림해주는 메소드이다.

 

우리는 메소드의 소비자로 주로 살아왔다. 나의 method를 직접 만들어주자. main 메소드의 키워드도 알아보자.

 


메소드의 기본형식

 

만약에 아래와 같은 코드가 있다고 하자.

              // 1억줄
              System.out.println("-");
              System.out.println("B");
              System.out.println("B");
              // 1억줄
              System.out.println("-");
              System.out.println("B");
              System.out.println("B");
              // 1억줄

이 때, B의 값을 일괄적으로 A로 바꾸고 싶을 때, 1억개의 줄을 하나하나 다 바꿀 수는 없는 노릇이다. 또한 이 1억줄의 코드를 추가해야 할 때도 1억줄의 코드를 낑겨넣어야 한다.

 

이 때, 저 과정을

printTwoTimesA();

라는 메소드를 바꿔보자.

public class WhyMethod {
       public static void printTwoTimesA() {
              System.out.println("-");
              System.out.println("A");
              System.out.println("A");
       }

       public static void main(String[] args) {

이렇게 main 상단에 선언을 하고 나서 뒤에 코드를 바꿔주자.

public class WhyMethod {
       public static void printTwoTimesA() {
              System.out.println("-");
              System.out.println("A");
              System.out.println("A");
       }

       public static void main(String[] args) {
              // 1억줄
              printTwoTimesA();
              // 1억줄
              printTwoTimesA();
              // 1억줄
              printTwoTimesA();
              // 1억줄
       }
}

코드가 훨씬 명료해졌다. 

 

한줄짜리 코드라고 해도 그 코드의 의미를 명확하게 하기 위해 메소드를 만드는 거는 좋은 습관이다.

 

추가적으로 자바 이클립스는 리팩토리 기능이 있다. 

 

코드를 드래그 -> Refactor -> Extract Method -> 메소드 이름 입력 -> public 체크 -> 프리뷰 -> ok

 

              System.out.println("-");
              System.out.println("A");
              System.out.println("A");
              System.out.println("-");
              System.out.println("A");
              System.out.println("A");
              System.out.println("-");
              System.out.println("A");
              System.out.println("A");

드래그 하고 리팩토리를 하면

              printTwoTimesA();
              printTwoTimesA();
              printTwoTimesA();

       public static void printTwoTimesA() {
              System.out.println("-");
              System.out.println("A");
              System.out.println("A");
       }

로 변한다.


메소드의 Input

 

 

그런데 이 메소드의 출력값을 "B" 바꾸려고 하면

              printTwoTimesA();
              printTwoTimesA();
              printTwoTimesA();
              printTwoTimesB();


       public static void printTwoTimesA() {
              System.out.println("-");
              System.out.println("A");
              System.out.println("A");
       }


       public static void printTwoTimesB() {
              System.out.println("-");
              System.out.println("B");
              System.out.println("B");
       }

이렇게 바꿔야 한다. 근데 뭔가가 불편해보인다.

 

다른 좋은 method는 입력값에 따라 다양한 모습들을 보여준다. 하지만 우리가 만든 printTwoTimesA()의 method는 리플레이 수준의 같은 출력값만을 갖는다.

 

그러면 입력값에 따라 다르게 출력되는 메소드를 만들어주자.

public class WhyMethod {
       
       
       public static void main(String[] args) {
              // 1억줄
              printTwoTimes("a", "=");
              // 1억줄
              printTwoTimes("a", "==");
              // 1억줄
              printTwoTimes("b", "===");
              // 1억줄
              printTwoTimes("c", "===");
              printTwoTimes("d", "====");
              
       }
              public static void printTwoTimes(String text, String delimiter) {
                     System.out.println(delimiter);
                     System.out.println(text);
                     System.out.println(text);
                     
                     
              }
}

그러면 이제

       public static void main(String[] args) {

요놈을 해석해 보자.

 

우리는 main이라는 method가 있어야 java는 실행하고 싶은 프로그램을 실행시킨다.

 

String[] 은 서로 연관된 문자열을 그룹핑하는 문자열 배열이다.

 

args는 main이라고 하는 메소드를 자바가 실행할 때, 입력해주는 입력값이 args를 통해 들어온다.

 

이 args의 값은 run configuratuin Argument를 통해 넣어줄 수 있다.

 

args는 args[0]과 args[1] 등등을 포함하는 개념이다. 이 값은 클래스 안에서 사용할 수 있다.

                                            // 매개변수, parameter
              public static void printTwoTimes(String text, String delimiter)

또한, 용어를 정리해보자. 아까 사용한 text와 delimiter라는 변수는 method 바깥쪽에서 method를 사용하는 값으로 주입하는 값으로 사용하는 매개자다 해서 매개변수, parameter라고 부른다.

                            //인자, argument
              printTwoTimes("a", "=");

여기에 함수 안으로 주입한 구체적인 값 a, =는 인자, argument라고 부른다.

 


 

메소드의 output

 

void는 return 값이 없을 때 사용한다.

       public static String a() {
              // ... 긴 코드가 있다.
              return "a";
       }

가령 a() 메소드를 사용한다고 했을 때, 문자열 a의 값이 돌아온다.

       public static int one() {
              return 1;
       }

또, one() 메소드를 사용했을 때, int 1이 반한된다. 이 때 각각의 a(), int() 앞에 String, int의 데이터타입을 명시해줘야 한다.

 

return 값이 있는 경우는 어떠한 메소드의 boolean 값이 true거나 false일 때 많이 쓸 것 같다. 하지만 연산이나 공정만 해주는 메소드도 있다. 이러한 메소드는 어떠한 값이 나올지 정해지지 않는 경우가 많다. 왜냐하면 메소드 안에서 String이 나오는지, int가 나오는지 그때그때 바꿔줄 수 있기 때문이다. 메소드의 데이터타입을 정해줄 수 없는데 이러한 경우 void를 쓴다.

 

return이 오면 메소드는 끝난다. return이 나오면 메소드의 데이터 타입을 정해줘야 한다. 

 

       public static String twoTimes(String text, String delimiter) {
              String out = "";
              out = out + delimiter + "\n";
              out = out + text + "\n";
              out = out + text + "\n";
              return out;

메소드는 입력값이 있고 출력값이 있다. 어떤 값을 리턴할지 직접 정해줘야 한다.


메소드의 활용

 

코드를 입력할 때, 주석을 잘 써줘야 한다. 또한 메소드를 이용해서 처리방법에 이름을 부여해서 정리정돈을 해줘야 한다. 낯선 코드도 단박에 활용 방법을 이해할 수 있다. void로 되어있는 메소드는 data type을 정해주면 정리가 쉽다.

public class AccountingApp {
       public static double getVat() {
              return valueOfSupply * vatRate; // error
       }
       public static void main(String[] args) {
              
              // 공급가액
              double valueOfSupply = Double.parseDouble(args[0]);
              
              //부가가치세율
              double vatRate = 0.1;

이렇게 코드를 작성하면 에러가 난다. valueOfSupply는 main method 안에서만 사용할 수 있는 지역변수이다. 이 변수를 AccountingApp이라는 모든 클래스가 사용할 수 있게 하려면 main method 밖, AccountingApp 안에있는 위치로 이동시켜야 한다.

 

우리가 method를 이용해서 코드의 가독성을 획기적으로 높일 수 있고, 한 번 작성한 코드를 호출만 하면 되기 때문에 재사용할 수 있고, 원래 내용의 본문 내용을 바꾸면 그 메소드를 바꾼 사용한 모든 코드의 변경이 가능하기 때문에 유지 보수의 편리성이 높아진다.

 


 

 

메소드는 서로 연관된 명령을 그루핑해서 이름을 붙인 것이다.

 

클래스는 서로 연관된 메소드와 변수를 그룹핑해서 이름을 붙인 것이다.

 

많은 컴퓨터 언어가 클래스와 같은 개념을 사용하지 않는 경우가 많다. 메소드만으로도 충분히 혁명적인 단계이다.

 

충분히 써먹고 적용하려고 노력해라. 


객체 지향 프로그래밍

Object Oriented Programming

 

클래스를 복제해서 서로 다른 내부적인 상태를 가지고 있는 복제본들을 Instatnce라고 한다. Class와 Instance를 포괄적으로 Object(객체)라고 할 수있다. 이런 객체를 뼈대로 하고 프로그래밍을 하는 방법이 객체 지향 프로그래밍이다. 대부분의 소프트웨어가 객체 지향 프로그래밍 방법론에 따라서 만들어진다. 남들이 만든 라이브러리도 객체 지향 프로그래밍을 하는 경우가 많다. 

 

코드의 양이 많아지고 남들의 라이브러리를 사용해야 하는 경우가 많다면 객체 지향으로 만들어야 한다.


access level modifier

 

public이 무엇인가?

 

//public, protected, default(생략), private

 

메소드를 넣는 방법은 다음과 같다.

 

public class AccessLevelModifiersMethod {
              public static void hi() {
                     System.out.println("Hi");
              }
       public static void main(String[] args) {
              
              hi();
       }
}

이 메소드를 클래스로 넣는 방법은 다음과 같다.

class Greeting{
              public static void hi() {
                     System.out.println("Hi");
              }
}
public class AccessLevelModifiersMethod {
       
       public static void main(String[] args) {
              
              Greeting.hi();
       }
}

이 때, 

class Greeting{
              private static void hi() {
                     System.out.println("Hi");
              }
}
public class AccessLevelModifiersMethod {
       
       public static void main(String[] args) {
              
              Greeting.hi(); // error
       }
}

hi() 메소드에 private를 작성하면 main 메소드 안에 있는 hi()에 에러가 발생한다. 

 

같은 클래스 안에서만 사용할 수 있는 것이 private이다. 다른 클래스 안에 있는 클래스는 사용할 수 없다.

 

다른 사용자가 사용하게 하려고 공유의 용도로 만든 것이 public이다.

 

클래스 안에서 내부적으로 처리하게 만든 것이 private이다.

 

외부에서 접근하게 만드는 level이라고 구분하면 된다. 이것이 access level modifiers(public, protected, default(생략), private)이다.


static

 

static - class method

no static - instance method

 

기본 셋팅을 해준다. 참고로 delimeter는 '구분자'를 뜻한다.

public class staticMethod {
       
       public static void a(String delimeter) {
              System.out.println(delimeter);
              System.out.println("a");
              System.out.println("a");
       }
       
       public static void b(String delimeter) {
              System.out.println(delimeter);
              System.out.println("b");
              System.out.println("b");
              
       }
       public static void main(String[] args) {
              Print.a("-");
              Print.b("*");
       }
}

이 때, print라는 클래스가 없기 때문에 다음과 같이 클래스를 만들어준다. 또한, 메소드를 클래스안에 넣는다.

 

 

그런데 만약, Print.a("-")라는 메소드를 1억번 넣어야 한다면?

 

우리는 같은 함수를 1억번 입력을 해야한다. 이것을 어떻게 효율적으로 바꿔줄 수 있을까

              Print t1 = new Print();

우리는 Print()라는 메소드를 복제(new)해준다. 또한 그것을 Print라는 데이터 타입의 t1(변수)로 담아준다. t1은 Print라는 클래스의 분신이다.

 

t1.delimeter = "-"; 로 넣어주면 t1.a();에 구분자를 넣어주지 않아도 -가 들어간다.

              Print t1 = new Print();
              t1.delimiter = "-";
              t1.a();
              t1.b();

t2.delimeter = "*"; 로 넣어주면 t2.a();에 구분자를 넣어주지 않아도 *가 들어간다.

              Print t2 = new print();
              t2.delimiter = "*";
              t2.a();
              t2.b();

이 때, t1, t2를 instance라고 부른다.

 

하지만 모두 에러가 난다. 인스턴스에 delimiter의 변수의 값을 지정하려면 상단의 코드를 바꿔줘야 한다.

       public String delimiter;
              public static void a() {
                     System.out.println(this.delimiter);
                     System.out.println("a");
                     System.out.println("a");
              }
              
              public static void b() {
                     System.out.println(this.delimiter);
                     System.out.println("b");
                     System.out.println("b");          
       }
}

상단에서 delimiter의 data type을 String으로 선언을 해줬기 때문에 a, b 메소드 괄호안에 String delimiter 인자를 빼줄 수 있다. 그리고 아래에 있는 delimiter에 this.를 넣어준다. this.delimiter는 아래의 t1.delimiter = "-"; 의 코드에서 -를 뜻하게 된다. 즉 인스턴스에 있는 변수 인자값이라고 할 수 있다.

 

인스턴스의 소속일 때는 static을 빼줘야 한다. 하지만 메소드가 클래스 소속일 때는 static을 넣어줘야 한다.

 

static을 빼준 이후

              Print.a();

는 동작하지 않는다. 이유는 Print는 클래스 이기 때문이다.

 

class Print{
       
       public String delimiter;
              public void a() {
                     System.out.println(this.delimiter);
                     System.out.println("a");
                     System.out.println("a");
              }
              
              public void b() {
                     System.out.println(this.delimiter);
                     System.out.println("b");
                     System.out.println("b");          
       }
              public static void c(String delimiter) {
                     System.out.println(delimiter);
                     System.out.println("c");
                     System.out.println("c");          
       }
}

프린트 클래스 안에 해당 메소드를 넣어주고 static을 입력한다.

 

그러면

              Print.c("%");

메소드가 작동을 한다.