JVM에서 메서드 오버로딩

새로운 Java Challengers 블로그에 오신 것을 환영합니다 ! 이 블로그는 Java 프로그래밍의 까다로운 개념에 전념합니다. 이를 마스터하면 고도로 숙련 된 Java 프로그래머가 될 수 있습니다.

이 블로그의 기술을 익히려면 약간의 노력이 필요하지만 Java 개발자로서 일상적인 경험에 큰 변화를 가져올 것입니다. 핵심 Java 프로그래밍 기술을 올바르게 적용하는 방법을 알면 버그를 피하는 것이 더 쉬우 며 Java 코드에서 일어나는 일을 정확히 알고 있으면 버그를 추적하는 것이 훨씬 쉽습니다.

Java 프로그래밍의 핵심 개념을 마스터 할 준비가 되셨습니까? 그렇다면 첫 번째 Java Challenger를 시작해 보겠습니다!  

용어 : 메서드 오버로딩

과부하 라는 용어로 인해 개발자는이 기술이 시스템에 과부하를 줄 것이라고 생각하는 경향이 있지만 사실이 아닙니다. 프로그래밍에서 메서드 오버로딩 은 매개 변수가 다른 동일한 메서드 이름을 사용하는 것을 의미합니다. 

메서드 오버로딩이란 무엇입니까?

메서드 오버로딩 은 개발자가 동일한 클래스에서 동일한 메서드 이름을 여러 번 사용할 수 있지만 매개 변수는 다른 프로그래밍 기술입니다. 이 경우 메서드가 오버로드되었다고 말합니다. 목록 1은 매개 변수의 수, 유형 및 순서가 다른 단일 메소드를 보여줍니다.

Listing 1. 세 가지 유형의 메서드 오버로딩

 Number of parameters: public class Calculator { void calculate(int number1, int number2) { } void calculate(int number1, int number2, int number3) { } } Type of parameters: public class Calculator { void calculate(int number1, int number2) { } void calculate(double number1, double number2) { } } Order of parameters: public class Calculator { void calculate(double number1, int number2) { } void calculate(int number1, double number2) { } } 

메서드 오버로딩 및 기본 형식

목록 1에서 기본 유형 intdouble. 이러한 유형과 다른 유형에 대해 더 많이 작업 할 것이므로 잠시 시간을내어 Java의 기본 유형을 검토하십시오.

표 1. Java의 기본 유형

유형 범위 기본 크기 예제 리터럴
 boolean  참 또는 거짓  그릇된  1 비트  허위 사실
 byte  -128 .. 127  0  8 비트  1, -90, 128
 char  유니 코드 문자 또는 0 ~ 65,536  \ u0000  16 비트  'a', '\ u0031', '\ 201', '\ n', 4
 short  -32,768 .. 32,767  0  16 비트  1, 3, 720, 22,000
 int  -2,147,483,648 .. 2,147,483,647  0  32 비트  -2, -1, 0, 1, 9
 long  -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807  0  64 비트  -4000L, -900L, 10L, 700L
 float  3.40282347 x 1038, 1.40239846 x 10-45  0.0  32 비트  1.67e200f, -1.57e-207f, .9f, 10.4F
 double

 1.7976931348623157 x 10308, 4.9406564584124654 x 10-324

 0.0  64 비트  1.e700d, -123457e, 37e1d

메서드 오버로딩을 사용해야하는 이유는 무엇입니까?

오버로딩은 코드를 더 깨끗하고 읽기 쉽게 만들어 주며 프로그램의 버그를 방지하는데도 도움이 될 수 있습니다.

목록 1과는 달리, 여러 가지고 프로그램 상상 calculate()같은 이름으로 방법을 calculate1, calculate2, calculate3. . . 좋지 않죠? calculate()메서드를 오버로딩하면 변경해야하는 항목 인 매개 변수 만 변경하면서 동일한 메서드 이름을 사용할 수 있습니다. 또한 코드에서 함께 그룹화되어 있기 때문에 오버로드 된 메서드를 찾는 것도 매우 쉽습니다.

과부하가 아닌 것

변수의 이름을 변경하는 것은 과부하 가 아닙니다 . 다음 코드는 컴파일되지 않습니다.

 public class Calculator { void calculate(int firstNumber, int secondNumber){} void calculate(int secondNumber, int thirdNumber){} } 

또한 메서드 서명에서 반환 유형을 변경하여 메서드를 오버로드 할 수 없습니다. 다음 코드도 컴파일되지 않습니다.

 public class Calculator { double calculate(int number1, int number2){return 0.0;} long calculate(int number1, int number2){return 0;} } 

생성자 오버로딩

메서드와 동일한 방식으로 생성자를 오버로드 할 수 있습니다.

 public class Calculator { private int number1; private int number2; public Calculator(int number1) {this.number1 = number1;} public Calculator(int number1, int number2) { this.number1 = number1; this.number2 = number2; } } 

메서드 오버로딩 도전을 받아보세요!

첫 번째 Java Challenger에 대한 준비가 되셨습니까? 알아 보자!

다음 코드를주의 깊게 검토하여 시작하십시오.

Listing 2. 고급 메서드 오버로딩 문제

 public class AdvancedOverloadingChallenge3 { static String x = ""; public static void main(String... doYourBest) { executeAction(1); executeAction(1.0); executeAction(Double.valueOf("5")); executeAction(1L); System.out.println(x); } static void executeAction(int ... var) {x += "a"; } static void executeAction(Integer var) {x += "b"; } static void executeAction(Object var) {x += "c"; } static void executeAction(short var) {x += "d"; } static void executeAction(float var) {x += "e"; } static void executeAction(double var) {x += "f"; } } 

좋습니다. 코드를 검토했습니다. 출력은 무엇입니까?

  1. befe
  2. BFCE
  3. Efce
  4. aecf

여기에서 답을 확인하세요.

방금 무슨 일이 있었나요? JVM이 오버로드 된 메서드를 컴파일하는 방법

Listing 2에서 무슨 일이 일어 났는지 이해하려면 JVM이 오버로드 된 메서드를 컴파일하는 방법에 대해 알아야합니다.

우선, JVM은 지능적으로 게으르다 . 항상 메소드를 실행하는 데 최소한의 노력을 기울인다. 따라서 JVM이 오버로딩을 처리하는 방법에 대해 생각할 때 세 가지 중요한 컴파일러 기술을 염두에 두십시오.

  1. 확대
  2. 권투 (오토 박싱 및 언 박싱)
  3. Varargs

이 세 가지 기술을 경험해 본 적이 없다면 몇 가지 예가이를 명확히하는 데 도움이 될 것입니다. JVM은 주어진 순서대로 실행합니다 .

다음은 확대 의 예입니다 .

 int primitiveIntNumber = 5; double primitiveDoubleNumber = primitiveIntNumber ; 

확장 될 때 기본 유형의 순서는 다음과 같습니다.

라파엘 델 네로

다음은 오토 박싱 의 예입니다 .

 int primitiveIntNumber = 7; Integer wrapperIntegerNumber = primitiveIntNumber; 

이 코드가 컴파일되면 뒤에서 어떤 일이 발생하는지 확인하십시오.

 Integer wrapperIntegerNumber = Integer.valueOf(primitiveIntNumber); 

다음은 unboxing 의 예입니다  .

 Integer wrapperIntegerNumber = 7; int primitiveIntNumber= wrapperIntegerNumber; 

다음은이 코드가 컴파일 될 때 뒤에서 일어나는 일입니다.

 int primitiveIntNumber = wrapperIntegerNumber.intValue(); 

그리고 여기 varargs 의 예가 있습니다 . 참고 varargs항상 실행하는 것이 마지막이다 :

 execute(int… numbers){} 

varargs는 무엇입니까?

Used for variable arguments, varargs is basically an array of values specified by three dots (…) We can pass however many int numbers we want to this method.

For example:

execute(1,3,4,6,7,8,8,6,4,6,88...); // We could continue… 

Varargs is very handy because the values can be passed directly to the method. If we were using arrays, we would have to instantiate the array with the values.

Widening: A practical example

When we pass the number 1 directly to the executeAction method, the JVM automatically treats it as an int. That’s why the number doesn't go to the executeAction(short var) method.

Similarly, if we pass the number 1.0, the JVM automatically recognizes that number as a double.

Of course, the number 1.0 could also be a float, but the type is pre-defined. That’s why the executeAction(double var) method is invoked in Listing 2.

When we use the Double wrapper type, there are two possibilities: either the wrapper number could be unboxed to a primitive type, or it could be widened into an Object. (Remember that every class in Java extends the Object class.) In that case, the JVM chooses to wided the Double type to an Object because it takes less effort than unboxing would,  as I explained before.

The last number we pass is 1L, and because we've specified the variable type this time, it is long.

Video challenge! Debugging method overloading

Debugging is one of the easiest ways to fully absorb programming concepts while also improving your code. In this video you can follow along while I debug and explain the method overloading challenge:

Common mistakes with overloading

By now you’ve probably figured out that things can get tricky with method overloading, so let’s consider a few of the challenges you will likely encounter.

Autoboxing with wrappers

Java is a strongly typed programming language, and when we use autoboxing with wrappers there are some things we have to keep in mind. For one thing, the following code won't compile:

 int primitiveIntNumber = 7; Double wrapperNumber = primitiveIntNumber; 

Autoboxing will only work with the double type because what happens when you compile this code is the same as the following:

 Double number = Double.valueOf(primitiveIntNumber); 

The above code will compile. The first int type will be widened to double and then it will be boxed to Double. But when autoboxing, there is no type widening and the constructor from Double.valueOf will receive a double, not an int. In this case, autoboxing would only work if we applied a cast, like so:

 Double wrapperNumber = (double) primitiveIntNumber; 

Remember that Integer cannot be Long and Float cannot be Double. There is no inheritance. Each of these types--Integer, Long, Float, and Double--is a Number and an Object.

When in doubt, just remember that wrapper numbers can be widened to Number or Object. (There is a lot more to explore about wrappers but I will leave it for another post.)

Hard-coded number types in the JVM

When we don’t specify a type to a number, the JVM will do it for us. If we use the number 1 directly in the code, the JVM will create it as an int. If you try to pass 1 directly to a method that is receiving a short, it won’t compile.

For example:

 class Calculator { public static void main(String… args) { // This method invocation will not compile // Yes, 1 could be char, short, byte but the JVM creates it as an int calculate(1); } void calculate(short number) {} } 

The same rule will be applied when using the number 1.0; although it could be a float, the JVM will treat this number as a double:

 class Calculator { public static void main(String… args) { // This method invocation will not compile // Yes, 1 could be float but the JVM creates it as double calculate(1.0); } void calculate(float number) {} } 

Another common mistake is to think that the Double or any other wrapper type would be better suited to the method that is receiving a double. In fact, it takes less effort for the JVM to widen the Double wrapper to an Object instead of unboxing it to a double primitive type.

To sum up, when used directly in Java code, 1 will be int and 1.0 will be double. Widening is the laziest path to execution, boxing or unboxing comes next, and the last operation will always be varargs.

As a curious fact, did you know that the char type accepts numbers?

 char anyChar = 127; // Yes, this is strange but it compiles 

What to remember about overloading

Overloading is a very powerful technique for scenarios where you need the same method name with different parameters. It’s a useful technique because having the right name in your code makes a big difference for readability. Rather than duplicate the method and add clutter to your code, you may simply overload it. Doing this keeps your code clean and easy to read, and it reduces the risk that duplicate methods will break some part of the system.

What to keep in mind: When overloading a method the JVM will make the least effort possible; this is the order of the laziest path to execution:

  • First is widening
  • Second is boxing
  • Third is Varargs

What to watch out for: Tricky situations will arise from declaring a number directly: 1 will be int and 1.0 will be double.

Also remember that you can declare these types explicitly using the syntax of 1F or 1f for a float or 1D or 1d for a double.

That concludes our first Java Challenger, introducing the JVM’s role in method overloading. It is important to realize that the JVM is inherently lazy, and will always follow the laziest path to execution.

 

Answer key

The answer to the Java Challenger in Listing 2 is: Option 3. efce.

More about method overloading in Java

  • Java 101: Classes and objects in Java: A true beginner’s introduction to classes and objects, including short sections on methods and method overloading.
  • Java 101: Elementary Java language features: Learn more about why it matters that Java is a strongly typed language and get a full introduction to primitive types in Java.
  • Java 메소드의 매개 변수가 너무 많음, Part 4 : 메소드 오버로딩의 한계와 단점, 그리고 사용자 정의 유형과 매개 변수 객체를 통합하여 해결하는 방법을 살펴 봅니다.

이 이야기, "JVM에서의 메소드 오버로딩"은 원래 JavaWorld에 의해 출판되었습니다.