검색결과 리스트
Class에 해당되는 글 2건
- 2014.10.29 [swift] Swift 기초 - 3 (1)
- 2011.04.07 (1) 리플렉션(Reflection) (3)
글
[swift] Swift 기초 - 3
Swift 스터디 세 번째 시간입니다.
이번 시간에는
구조체와 클래스, 속성과 메서드,
서브스크립트와 상속, 생성자와 소멸자에 대해서 알아보겠습니다.
(생성자와 소멸자라고 번역하는게 맞는지 모르겠지만, 당분간 이 용어를 사용하겠습니다.)
클래스는 class 키워드를 사용하여 정의합니다.
신기하게도 Swift 에서는 클래스를 정의하면, 디폴트로 어떠한 클래스(NSObject와 같은...)도 상속하지 않습니다.
콜론: 을 사용하여 클래스 상속을 표현합니다.
운송수단이라는 클래스를 상속받아 자전거 클래스를 정의했습니다.
저장속성은 단순히 데이터를 담는 속성입니다.
Vehicle(운송수단) 클래스는 바퀴의 개수 저장속성과 승객수 저장속성을 가지고 있습니다.
계산속성은 저장속성을 바탕으로 계산되는 속성입니다.
자동차 클래스의 스피드 속성은 최저속도, 가속도 속성을 바탕으로
매번 계산되는 계산속성의 좋은 예입니다.
계산속성은 set, get 키워드를 사용하여 setter, getter 를 정의합니다.
운송수단 클래스에 바퀴수 저장속도를 사용하여 description 계산속성을 정의했습니다.
계산속성은 접근할 때마다 매번 가공되는(계산되는) 속성이라 할 수 있습니다.
계산속성은 선언시 반드시 타입을 정의해야 합니다.
운송수단 클래스를 사용하는 예제입니다.
멤버연산자(.dot)를 사용하여 저장속성과 계산속성에 접근합니다.
클래스는 디폴트로 init() 생성자 함수를 가지고 있습니다.
서브클래스는 생성자를 재정의(override) 할 수 있습니다.
운송수단 클래스를 상속하는 자전거 클래스는
init 생성자를 재정의하여 바퀴수를 2로 초기화합니다
계산속성은 메소드 처럼 서브클래스에서 재정의가 가능합니다.
운송수단 클래스에서 정의한 description 계산속성을
자동차 서브클래스에서 재정의하여 사용했습니다.
재정의한 계산속성을 사용하는 예제입니다.
Swift에서는 속성 옵저버를 제공하여 쉽게 옵저버 패턴을 구현할 수 있습니다.
계산속성을 정의할 때, willSet, didSet 블럭을 지정합니다.
속성이 변경되기 전, 변경된 후에 수행할 작업을 명시하면 됩니다.
willSet 블럭에서는 새롭게 변경될 속성 값을 newValue 변수로 접근할 수 있으며,
didSet 블럭에서는 변경되기 전 속성값을 oldValue 변수로 접근할 수 있습니다.
클래스의 메서드는 함수와 동일하게 func 키워드를 사용하여 클래스 내부에 정의합니다.
Swift에서 함수는 독특하게도, 내부 파라미터 이름과 외부 파라미터 이름을 갖습니다.
내부 파라미터 이름은 함수의 몸체 즉 내부에서 사용하는 이름입니다.
외부 파라미터 이름은 함수를 사용(호출)하는 곳에서 사용하는 이름입니다.
파라미터 이름 앞에 #을 사용하면 컴파일러는
내부 파라미터 이름과 외부 파라미터 이름을 동일하게 다룹니다.
각각 이름을 명시해야하는 번거로움을 피할 수 있습니다.
구조체 또한 클래스와 같이 속성과 메서드를 가질 수 있습니다.
구조체도 클래스처럼 생성자를 가지고 있습니다.
다만 구조체의 디폴트 생성자는, 파라미터 이름과 포함하고 있는 속성이름이 일치하는데,
member-wise 생성자라고 합니다.
구조체도 저장속성과 메서드를 정의할 수 있습니다.
이 시점에서 생기는 의문점!
"구조체가 속성과 메서드를 가질수 있다면 클래스와 다른게 무엇인가?"
핵심은 클래스는 참조로 전달되고, 구조체는 값이 복사 된다는 점입니다.
Window 클래스의 frame 속성을 할당한 newFrame의 속성을 변경해도, window.frame은 수정되지 않지만,
setup 함수로 전달된 window의 frame은 변경사항이 반영되는 것을 알수 있습니다.
var, let 키워드의 규칙은 기본형 뿐만 아니라 클래스, 구조체에도 일관되게 적용됩니다.
다만 조금의 차이는 있습니다.
let 으로 선언된 클래스 변수에는 다른 클래스 참조를 할당하면 컴파일 에러가 발생합니다.
let 으로 선언된 클래스 변수 자체가 상수가 됩니다.
let으로 선언된 구조체는 멤버 속성값을 변경할 수 없습니다.
즉 구조체의 멤버 속성들이 상수가 됩니다.
클래스는 참조, 구조체는 값이 복사된다는 특성을 알면 쉽게 이해할 수 있습니다.
재미있는 특성중에 하나로, 구조체의 메소드 내부에서는 속성을 변경할 수 없습니다.
속성을 수정하려면 명시적으로 mutating 키워드를 메서드 앞에 지정해야 합니다.
앞서 구조체는 member-wise 생성자를 갖는다고 이야기했습니다.
구조체도 커스텀 생성자를 정의할 수 있습니다.
다만 모든 속성은 사용하기 전에 초기화 되어야 합니다.
blue 속성을 초기화 하기 전에 validateColor() 함수를 호출하면
컴파일 에러가 발생합니다.
클래스의 모든 속성도 사용하기 전에 초기화 되어야 하는 것은 동일합니다.
모든 변수는 사용하기 전에 초기화되야 한다는 것은 Swift 언어의 공통된 규칙이지요.
기본 생성자 외에 편의상의 목적으로 조금씩 다른 여러 생성자를 정의할 수 있습니다.
Swift에서는 이런 목적의 생성자에 친절하게도 convenience 키워드를 제공합니다.
클래스 설계자의 의도대로 생성되도록 제공하는, 의도된(designated) 생성자 외에
편의성을 위해 제공하는 생성자라 생각하면 됩니다.
보통 convenience 생성자는 다른 생성자를 다시 호출하는 체이닝(chaining)을 통해 구현됩니다.
소멸자는 클래스의 메모리가 해제되는 시점에 사용한 자원을 정리하는 역할을 수행하는 함수입니다.
파일을 열어 사용했다면, 소멸자에서 파일을 닫도록 명시해 의도치 않은 메모리 누수를 막을 수 있습니다.
필요치 않은 자원을 조기부터 낭비하는 것을 막기위해,
실제 자원을 사용할때 메모리를 할당하는 레이지 로딩이라는 기법이 있습니다.
Swift에서는 속성에 lazy 키워드를 사용하여 레이지 로딩 기법을 제공합니다.
lazy로 선언된 MultiplayerManager 객체는
실제로 객체가 최초로 사용 될때(multiplayerManager.addPlayer(player) 호출 시점에) 메모리에 할당됩니다.
서브스크립트는 구조체나 클래스의 속성을 인덱스를 제공하여 접근할 수 있는 인터페이스를 제공합니다.
subscript 키워드를 사용하여, get, set 블럭을 정의합니다.
인덱스 형태로 접근하면 다루기 쉬운 특정 데이터 구조를 다룰때 편리한 문법입니다.
대표적으로 행렬을 예로 들수 있습니다.
구구단을 출력하기 위해 TimesTable 구조체에 서브스크립트를 정의하여 사용하는 예제입니다.
서브스크립트를 사용했을 때의 2가지 장점을 엿볼 수 있습니다.
첫째는 속성에 접근하는 단순화된 문법과 둘째는, 메모리 절약입니다.
3단을 출력하기 위한 모든 데이터를 저장 할 필요없이
인덱스와 단수 변수만을 사용하여 메모리를 절약했습니다.
행렬 구조체에 서브스크립트를 정의하고 있습니다.
메소드를 정의하여 똑같이 구현할수 있는 기능이지만
서브스크립트를 사용하면 쉽고 가독성이 좋습니다.
'Swift' 카테고리의 다른 글
| [swift2] New in Swift2.0 (0) | 2015.06.19 |
|---|---|
| [swift] Apple Swift Blog (0) | 2015.06.10 |
| [swift] Swift 기초 - 4 (0) | 2014.10.29 |
| [swift] Swift 기초 - 3 (1) | 2014.10.29 |
| [swift] Swift 기초 - 2 (0) | 2014.10.29 |
| [swift] Swift 기초 - 1 (1) | 2014.10.29 |
설정
트랙백
댓글
글
(1) 리플렉션(Reflection)
작성할 수 있다. 하지만 리플렉션을 사용하면 좀더 유연한 프로그램을 작성할 수 있다. 자바에서 리플렉션을
이해하기 위해서, 자바 클래스 파일은 바이트 코드로 컴파일 되며 실행시간에 이 바이트 코드가 해석되어
실행된다는 것을 아는 것이 첫 출발점이 된다. 이 바이트 코드에는 클래스에 대한 모든 정보를 포함하고 있다.
클래스 파일이 있는 위치와 이 클래스 파일의 이름을 알수 있다면 언제든지 바이트 코드를 뒤져서 클래스에 대한
정보를 얻어낼 수 있다. 이제부터 리플렉션을 통해 어떻게, 어떤 정보를 얻을 수 있는지 알아보자.
1. 리플렉션(Reflection)
클래스를 사용할 수 있는 기법을 의미한다. 마치 거울에 비친 모습과 유사하여 리플렉션이란 이름을 붙힌 것 같다.
2. 리플렉션을 사용하는 이유
위임 클래스를 리플렉션을 통해 동적/정적으로 생성하고 교체하는 방식으로 사용된다. 프레임워크에서 유연성이 있는 동작을
위해 자주 사용되는 방식이기도 하다.
3. 리플렉션을 통해 얻을 수 있는 정보
ClassName Class Modifiers (public, private, synchronized 등) Package Info Superclass Implemented Interfaces Constructors Methods Fields Annotations
3.1 Class Object
프리미티 타입과 배열 타입을 포함하여 자바의 모든 타입들은 연관된 Class 객체를 가지고 있으며, 컴파일 타임에
클래스의 이름을 알수 있다면, 다음과 같이 Class 객체를 얻을 수 있다.
Class myObjectClass = MyObject.class컴파일 타임에 이름을 알수 없다면, 런타임에 문자열로 된 이름으로 부터 클래스 객체를 아래와 같이 얻을 수 있다.
String className = ... // 클래스 풀네임 Class myObjectClass = Class.forName(className);이때 문자열로 된 클래스 이름은 패키지 경로까지 포함한 풀네임이여야 하며, 해당 패키지에 클래스가 존재하지 않으면
Class.forName 메소드는 ClassNotFoundException 예외를 던지게 된다.
3.1 Class Name
있고, getSimpleName() 을 사용하여 패키지가 포함되지 않은 클래스 이름을 얻을 수 있다.
// 클래스 풀네임 Class aClass = ... // 이전에 얻은 클래스 객체 String className = aClass.getName();
// 클래스 심플 네임 Class aClass = ... // 이전에 얻은 클래스 객체 String simpleClassName = aClass.getSimpleName();
3.2 Modifier
통해 해당 플래그가 켜져있는지 확인할 수 있다.
// 변경자 얻기 Class aClass = ... // 이전에 얻은 클래스 객체 int modifiers = aClass.getModifiers();
// 변경자 플래그 확인 메소드들 Modifier.isAbstract(int modifiers) Modifier.isFinal(int modifiers) Modifier.isInterface(int modifiers) Modifier.isNative(int modifiers) Modifier.isPrivate(int modifiers) Modifier.isProtected(int modifiers) Modifier.isPublic(int modifiers) Modifier.isStatic(int modifiers) Modifier.isStrict(int modifiers) Modifier.isSynchronized(int modifiers) Modifier.isTransient(int modifiers) Modifier.isVolatile(int modifiers)
3.3 Package Info
다음과 같이 Class 객체로부터 패키지에 대한 정보를 얻는다.
// 패키지 정보 얻기 Class aClass = ... // 이전에 얻은 클래스 객체 Package package = aClass.getPackage();
Manifest 파일에서도 이 패키지에 대한 특정한 정보를 얻을 수 있다.
(예를 들면 Manifest 파일에 지정된 패지키 버전 번호 같은...)
3.4 Superclass
아래와 같이 수퍼클래스의 class 객체를 얻을 수 있다.
// 수퍼 클래스의 class 객체 얻기 Class superclass = aClass.getSuperclass();
3.5 Implemented Interfaces
클래스 객체에 의해 구현된 인터페이스의 목록을 얻어보자.
// 구현한 인터페이스 목록 얻기 Class aClass = ... // 이전에 얻은 클래스 객체 Class[] interfaces = aClass.getInterfaces();
해당 인터페이스는 목록에 포함되지 않는 것에 주의하자. 구현하는 완전한 인터페이스의 목록을 얻기 위해서는
자신의 수퍼클래스의 구현 인터페이스 목록을 재귀적으로 확인해야 한다.
3.6 Constructors
다음과 같이 클래스의 생성자 목록에 접근한다.
// 클래스 생성자 목록 얻기 Constructor[] constructors = aClass.getConstructors();
3.7 Methods
다음과 같이 클래스의 메소드들에 접근한다.
// 메소드 목록 얻기 Method[] methods = aClass.getMethods();
3.8 Fields
다음과 같이 클래스의 멤버 변수들에 접근한다.
// 필드 목록 얻기 Field[] fields = aClass.getFields();
3.9 Annotations
다음과 같이 클래스의 어노테이션에 접근한다.
// 어노테이션 목록 얻기 Annotation[] annotations = aClass.getAnnotations();
※ 리플렉션을 사용하여 Annotation을 처리하는 것은 아래 포스트를 참고!!
Java-어노테이션(Annotation)
Java-어노테이션 사용하기
4. 리플렉션 사용 예
Board 클래스에 대한 정의는 아래와 같다.
package com.tistory.hiddenviewer.reflection;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
public class Board implements ActionListener{
public final static String boardName = "MyBoard";
public ArrayList boardList;
public int seq;
protected String title;
private String contents;
public Board() {
this(10);
}
public Board(int count) {
this.boardList = new ArrayList(count);
}
public int getSeq() {
return seq;
}
public void setSeq(int seq) {
this.seq = seq;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContents() {
return contents;
}
public void setContents(String contents) {
this.contents = contents;
}
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
}
}
Board 클래스에 대한 정보를 리플렉션을 사용하여 출력하였다.
package com.tistory.hiddenviewer.reflection.executor;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public class BoardReflectionExecutor {
public static void main(String[] args) throws IOException, ClassNotFoundException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.print("생성할 클래스 이름을 입력하세요(패키지 포함): ");
String className = br.readLine();
Class cls = Class.forName(className);
// 클래스 이름얻기
String classFullName = cls.getName();
String classSimpleName = cls.getSimpleName();
System.out.println("class full name: " + classFullName);
System.out.println("class simple name: " + classSimpleName);
// 변경자 얻기
int modifiers = cls.getModifiers();
if (Modifier.isPublic(modifiers)) {
System.out.println("class is public class");
}
if (Modifier.isFinal(modifiers)) {
System.out.println("class is final class");
}
// 패키지 얻기
Package pkg = cls.getPackage();
System.out.println("package name: " + pkg.getName());
// 수퍼클래스 얻기
Class superCls = cls.getSuperclass();
System.out.println("super class name :" + superCls.getName());
// 구현 인터페이스 목록 얻기
Class[] interfaces = cls.getInterfaces();
for (Class cs : interfaces) {
System.out.println("this class implements " + cs.getName() + " interface");
}
// 생성자 목록 얻기
Constructor[] conturctors = cls.getConstructors();
for (Constructor constructor : conturctors) {
System.out.println("Constructor: " + constructor.getName());
}
// 메서드 목록 얻기
Method[] methods = cls.getMethods();
for (Method method : methods) {
System.out.println(method.getReturnType() + " " + method.getName() + "(...)");
}
// 프로퍼티 목록 얻기
Field[] fields = cls.getFields();
for (Field field : fields) {
System.out.println(field.getType() + " " + field.getName());
}
// 어노테이션 얻기
Annotation[] annotations = cls.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation.toString());
}
}
}
출력결과
다음 포스팅에서는 리플렉션을 사용하여 실세로 해당 타입의 객체를 생성 / private 필드에 접근 / 메소드를 호출 하는 방법에
대해 알아보자.
'언어로그 > Java' 카테고리의 다른 글
| 리플렉션으로 Getter 와 Setter 검사하기 (0) | 2011.04.07 |
|---|---|
| (2) 리플렉션(Reflection) 사용하기 (0) | 2011.04.07 |
| (1) 리플렉션(Reflection) (3) | 2011.04.07 |
| 예외처리 (Exception Handling) (0) | 2011.03.24 |
| [Java] 어노테이션 사용하기 (1) | 2011.03.19 |
| [Java] 어노테이션(Annotation) (2) | 2011.03.15 |