객체 지향 프로그래밍
클래스
이미 객체 지향 프로그래밍에 대해 친숙하다고 가정하고 클래스의 구조에 대해 빠르게 설명하겠습니다 :
package my.pack; /* my.pack.MyClass 라는 클래스를 정의합니다. */ class MyClass { // .... }
클래스는 여러 속성과 메서드를 가질 수 있습니다.
package my.pack; class MyClass { var id : Int; static var name : String = "MyString"; function foo() : Void { } static function bar( s : String, v : Bool ) : Void { } }
속성과 메서드는 다음과 같은 플래그들을 가질 수 있습니다 :
- static : 이 클래스의 인스턴스가 갖지 않고 클래스 자신이 갖는 필드입니다. Static 식별자는 클래스 안에서는 바로 사용될 수 있지만 클래스 바깥에서는 클래스 이름과 함께 사용되어야 합니다.( 예 :
my.pack.MyClass.name) - dynamic: 동적으로 재설정 될 수 있는 필드입니다.
- override: 상위 클래스의 멤버를 오버라이드한 필드입니다.
- public : 클래스의 외부에서 접근할 수 있는 필드입니다.
- private : 클래스 자신이나 이 클래스를 상속받는 클래스에서만 접근할 수 있는 필드입니다.
클래스의 내부 접근이 허용되지 않음이 보장됩니다.
기본적으로, 모든 필드는private필드입니다. 이는 Java나 PHP등 여러 객체지향 언어에서 사용되는 protected 키워드에 해당합니다.
모든 클래스 변수는 무조건 타입이 선언되어야 합니다( 사용할 값의 타입을 알 수 없는 경우는 Dynamic 타입을 사용하면 됩니다 ). 함수 인자와 반환형은 선택적이지만 그 것들도 엄격한 타입 검사가 이루어지는데, 타입 추론에서 소개하겠습니다.
정적변수 외에는 초기화 값을 가질 수 없습니다. 정적변수는 초기화 값을 가질 수 있지만 필수는 아닙니다.
생성자
클래스는 비 정적함수인 new라고 불리는 단 하나의 생성자만을 가질 수 있습니다. 이는 클래스 함수를 호출하는 키워드로도 사용됩니다 :
class Point { public var x : Int; public var y : Int; public function new() { this.x = 0; this.y = 0; } }
생성자 인자설정 & 오버로딩 :
public function new( x : Int, ?y : Int ) { this.x = x; this.y = (y == null) ? 0 : y; // "y" is optional }
클래스 상속
클래스가 선언될 때에 하나의 클래스를 확장할 수 있고 여러 클래스나 인터페이스를 구현할 수 있습니다. 하나의 클래스에서 동시에 여러 타입을 상속받을 수 있고, 그들과 동일하게 사용될 수 있습니다 :
class D extends A, implements B, implements C { }
D의 모든 인스턴스는 D 타입을 갖지만, A, B나 C 타입이 필요한 곳에서도 사용될 수 있습니다. 따라서 D의 모든 인스턴스는 A, B, C, D 타입을 다 갖습니다.
Extends
When extending a class, your class inherits from all public and private non-static fields. You can then use them in your class as if they where declared here. You can also override a method by redefining it with the same number of arguments and types as its superclass. Your class can not inherit static fields.
When a method is overridden, then you can still access the superclass method using super :
class B extends A { override function foo() : Int { return super.foo() + 1; } }
In your class constructor you can call the superclass constructor using also super :
class B extends A { function new() { super(36,""); } }
Implements
When implementing a class or an interface, your class is required to implement all the fields declared or inherited by the class it implements, with the same type and name. However the field might already be inherited from a superclass.
Interfaces
An Interface is an abstract type. It is declared using the interface keyword. By default, all interface fields are public. Interfaces cannot be instantiated.
interface PointProto { var x : Int; var y : Int; function length() : Int; }
An interface can also implement one or several interfaces :
interface PointMore implements PointProto { function distanceTo( p : PointProto ) : Float; }
Helper Classes
In Haxe, it is possible to have more than one class definition per class file:
// both definitions in Foo.hx class Foo { ... } class FooHelper { ... }
This is fairly common in many object oriented languages such as Java. However, unlike other such languages, Haxe also allows for these internal helper classes to be publicly available outside of the main class. By importing the main class, the helper class becomes available:
// in Bar.hx import Foo; class Bar{ var b:FooHelper; }
The helper class can also be made accessible via an extended package declaration:
// in Bar.hx class Bar{ var b:Foo.FooHelper; }
The use of public internal helper classes is typically not a recommended practice in most languages. Since helper class names do not correspond with their .hx file names, they can be harder to find in source trees.
HaXe lets you mark an internal helper class with the private keyword, in the same way that fields are marked:
// both definitions in Foo.hx class Foo { ... } private class FooHelper { ... }
This prevents the internal helper class from being accessed outside of the main class.
Unless there is a good reason for their use, consider placing each public class definition in its own .hx file, or make the internal class private, to prevent its definitions being included (and possibly causing a name conflict) when the main class is imported.