Java Classes (2)
Nested Classes
Java에서는 Class 내부에 또 다른 class를 선언할 수 있는데, 이를 Nested class라 한다. 이는 또 static nested class와 non-static nested class(inner class)로 나뉜다. 이러한 기능은 관련 있는 class들을 논리적으로 묶을 수 있다. 또한 inner class로 선언된 class에서 바깥 class의 private 필드에 접근할 수 있으므로, private 필드에 접근할 수 있는 class와 없는 class를 구분하여 전반적인 소프트웨어의 encapsulation 정도를 높일 수 있다.
Inner class와 Static nested class 및 그 instance는 다음과 같이 선언하고 생성한다.
class OuterClass {
class InnerClass {
// ...
}
static class StaticNestedClass {
//
}
}
OuterClass outer = new OuterClass();
OuterClass.InnerClass inner = OuterClass.new InnerClass();
StaticNestedClass staticNested = new StaticNestedClass();
만약 inner class의 field나 method 이름이 outer class와 같다면 inner class의 것이 기존을 덮어쓰게 된다. 이를 shadowing이라 한다.
Local classes, Anonymous classes
Local class란 block 내부에서 선언되어 사용되는 class를 의미한다. 이름에서 알 수 있듯이 block 밖에서는 사용할 수 없으며, static한 멤버를 가질 수 없다.
Anonymous class는 마찬가지로 특정 method 내부에서 선언되고 사용되는 class인데, 이름을 주지 않고 class를 선언함과 동시에 instantiate할 수 있다. 다음 예시에서와 같이, new 연산자와 Interface 이름, constructor의 parameter와 class declaration body를 이용해 사용한다.
public class Main {
public static void main(String... args) {
HelloWorldAnonymousClasses myApp =
new HelloWorldAnonymousClasses();
myApp.sayHello();
}
}
class HelloWorldAnonymousClasses {
interface HelloWorld {
public void greet();
public void greetSomeone(String someone);
}
public void sayHello() {
HelloWorld englishGreeting = new HelloWorld() { // Anonymous class
String name = "world";
public void greet() {
greetSomeone("world");
}
public void greetSomeone(String someone) {
name = someone;
System.out.println("Hello " + name);
}
};
englishGreeting.greet();
}
}
만들고자 하는 Anonymous class가 단일 method로 이루어진 interface를 implement하는 경우, Lambda expression을 사용할 수 있다. Lambda expression은 괄호로 둘러싸인 해당 method의 parameter와, 화살표 기호(->), 그리고 구현하는 method의 내용을 block 안에 적는다. 예시는 다음과 같다.
public class Main {
public static void main(String... args) {
Calculator myApp = new Calculator();
Calculator.IntegerMath addition = (a, b) -> a + b;
Calculator.IntegerMath subtraction = (a, b) -> a - b;
Calculator.IntegerMath multiplication = (a, b) -> a * b;
System.out.println(myApp.operateBinary(40, 2, addition)); // 42
System.out.println(myApp.operateBinary(40, 2, subtraction)); // 38
System.out.println(myApp.operateBinary(40, 2, multiplication)); // 80
}
}
class Calculator {
interface IntegerMath {
int operation(int a, int b);
}
public int operateBinary(int a, int b, IntegerMath op) {
return op.operation(a, b);
}
}
위의 addition, subtraction, multiplication은 IntegerMath interface를 implement하는 anonymous class의 instance가 된다. method가 하나( operation(int a,int b) )밖에 없으므로, 그 method의 구현을 lambda expression으로 나타내는 것이다.
Anonymous class와 Lambda expression은 어떤 간단한 기능을 encapsulation하고 싶을 때 요긴하다.
Enum types
Enum type은 미리 정의된 상수들로만 그 값을 가질 수 있는 특별한 자료형이다. Enum을 정의하게 되면, enum은 일종의 class와 같이 작동하며, field와 method를 가질 수 있다.
public enum Planet {
MERCURY (3.303e+23, 2.4397e6),
VENUS (4.869e+24, 6.0518e6),
EARTH (5.976e+24, 6.37814e6),
MARS (6.421e+23, 3.3972e6),
JUPITER (1.9e+27, 7.1492e7),
SATURN (5.688e+26, 6.0268e7),
URANUS (8.686e+25, 2.5559e7),
NEPTUNE (1.024e+26, 2.4746e7);
private final double mass; // in kilograms
private final double radius; // in meters
Planet(double mass, double radius) {
this.mass = mass;
this.radius = radius;
}
}
Interfaces and Inheritance
이제 interface와 상속에 대해 자세히 알아보자. Interface는 class와 같은 reference type이다. 즉 class처럼 reference type 변수를 선언하는 데에도 사용할 수 있다. 그러나 interface는 상수와 method signature, default method, static method, 그리고 nested type만을 멤버로 가질 수 있다. Interface에서 Static method와 다르게, default method는 상속될 수 있다.
Class가 다른 superclass를 상속할 때, 그 subclass에서는 superclass의 method와 같은 signature를 가지는 method를 새로 만들어 덮어씌울 수 있다. Instance method를 덮어씌우는 경우 overriding한다고 하며, static method의 경우 hiding한다고 부른다. 이렇듯 부모 class에서 정의된 method는 자식 class에서 그 기능이 조금씩 변형되어 나타날 수 있는데, 이를 polymorphism이라 한다. 또한 부모 클래스의 method 구현이 자식에서 바뀌길 원하지 않는 경우, method 정의에 final modifier를 붙여 method의 기능을 고정시킬 수 있다.
Java에서, 모든 object의 superclass는 Object class이다. 우리가 정의하는 class는 기본적으로 이를 상속받으므로, equals(Object obj), getClass(), hashCode() 등의 method들을 기본적으로 사용할 수 있다.
또한, instance를 만들 수 없지만 다른 class에게 상속만 될 수 있는 종류의 class도 종류하는데, 이를 abstract class라고 한다. 이들은 선언만 되어있고 구현은 채워지지 않은 abstract method들을 method로 가진다. Abstract class는 주로 서로 관련이 깊은 여러 class들만을 상속받아 정의하고 싶을 때 interface 대신 사용할 수 있다.