반응형
<출처 : http://javaora.tistory.com/entry/Learning-the-JavaFX-Script-Programming-Language-Lesson-11-Access-Modifiers >

목차
- 기본 접근자
- package 접근자
- protected 접근자
- public 접근자
- public-read 접근자
- public-init 접근자


- 기본 접근자
기본접근자는 "scipt-only"로서 특별한 접근자 키워드를 제공하지 않는다. 

var x;
var x : String;
var x = z + 22;
var x = bind f(q);

기본 접근자는 같은 스크립트 파일 내에서는 얼마든지 접근이 가능하며 그외에는 모든 접근이 불가능하다.


- package 접근자
package 접근자는 이름에서 알 수 있듯 같은 패키지 내에서는 변수, 함수, 클래스의 접근이 가능하다.

// Inside file tutorial/one.fx
package tutorial; // places this script in the "tutorial" package
package var message = "Hello from one.fx!"; // this is the "package" access modifier
package function printMessage() {
     println("{message} (in function printMessage)");
}

// Inside file tutorial/two.fx
package tutorial;
println(one.message);
one.printMessage();

위 소스를 tutorial 이라는 디렉토리를 만들어 각각 one.fx 와 two.fx라는 파일로 저장하자.

javafxc tutorial/one.fx tutorial/two.fx
javafx tutorial/two

두개의 파일을 컴파일후 two 파일을 실행해보자.

Hello from one.fx!
Hello from one.fx! (in function printMessage)

예를 보면 둘다 tutorial이란 패키지 명을 생성되어있으며 two.fx 파일에서 one.fx 파일의 변수와 함수를 사용함에 있어 문제가 없음을 알 수 있다.


- protected 접근자
protected 접근자는 같은 패키지 내에서 또한 해당 클래스를 상속받은 서브클래스에서의 접근만을 허용한다.


- public 접근자
public 접근자는 이전 레슨을 통해서 보았듯이 접근에 제한이 없다.


- public-read 접근자
public-read 접근자는 변수의 읽기 기능만 public으로 선언하며 쓰기 기능은 같은 스크립트 파일내에서만 가능하다. 추가적으로 두개의 접근자를 이용하여 쓰기 기능의 접근자를 지정 할 수도 있다. 가령 package public-read 또는 protected public-read 와 같이 접근자를 쓴다면 쓰기기능에 있어서는 package나 protected의 접근성을 제공한다는 의미이다. 예를 보도록 하자.

// Inside file tutorial/one.fx
package tutorial;
public-read var x = 1;

// Inside tutorial/two.fx
package tutorial;
println(one.x);

아까 작성한 예제 파일 one.fx와 two.fx를 다음과 같이 수정해 보자. 그리고 컴파일 후 실행해보자.

javafxc tutorial/one.fx tutorial/two.fx
javafx tutorial/two

1

그 결과는 쉽게 예상 가능 할 것이다. 그럼 two.fx 예제를 다음과 같이 수정해보자.

// Inside tutorial/two.fx
package tutorial;
one.x = 2; 
println(one.x);

public-read 접근자 속성이 부여된 변수를 수정해보려고 하면 어떻게 될까? 컴파일하면 다음과 같은 에러메시지가 나온다.

tutorial/two.fx:3: x has script only (default) write access in tutorial.one
one.x = 2;
   ^
1 error

그럼 이번엔 one.fx 파일의 x 변수의 쓰기 권한을 package로 바꿔보자.

// Inside file tutorial/one.fx
package tutorial;
package public-read var x = 1;

// Inside tutorial/two.fx
package tutorial;
one.x = 2;
println(one.x);

컴파일후 실행하면 바뀐 값인 "2"가 출력될 것이다. 위에 언급한대로 쓰기에 package 접근속성을 부여 하였으므로 같은 tutorial 패키지 내 이므로 그 수정이 가능하다.


- public-init 접근자
public-init 접근자는 변수에 초기화 기능에만 public 접근 속성을 부여한다. 그러나 처음 초기화 이외의 쓰기 권한은 public-read와 같은 방식으로 이용해야 한다. 또한 이 변수의 읽기 접근 속성값은 public 이다. 예를 보도록 하자.

// Inside file tutorial/one.fx
package tutorial;
public class one {
     public-init var message; //초기화 접근 속성만을 제공함.
}

// Inside file two.fx
import tutorial.one;
var o = one {
     message: "Initialized this variable from a different package!"
}
println(o.message);//읽기 속성은 public 이므로 아무런 문제가 없다.

컴파일후 실행하면 예상하듯 "Initialized this variable from a different package!" 가 출력된다.

그럼 이미 초기화된 값을 수정하려 한다면 어떻게 될까?

// Inside file two.fx
import tutorial.one;
var o = one {
     message: "Initialized this variable from a different package!"
}
o.message = "Changing the message..."; // WON'T COMPILE
println(o.message);

two.fx:12: message has script only (default) write access in tutorial.one
o.message = "Changing the message..."; // WON'T COMPILE
 ^
1 error

예상대로 해당 수정부분에서 에러가 발생한다. 이부분에 에러가 발생되지 않도록 하려면 위 public-read 예에서 봤듯 쓰기 가능한 접근 권한인 package나 protected 접근 속성을 부여하면 된다.


이것으로 10회에 걸친 JavaFX의 간단한 문법에 대해 알아 보았다. 대부분 Java나 Javascript와 비슷한 문법을 택하였기 때문에 원문의 설명과 그 예제 또한 간단간단하게만 나와있다. 

작성된 글이 믿음직스럽지 않다면.. ^^;; 원문으로 읽어보는 것도 도움이 될 듯 싶다.

참고 :
1. Learning the JavaFX Script Programming Language

반응형
<출처 : http://javaora.tistory.com/entry/Learning-the-JavaFX-Script-Programming-Language-Lesson-10-Packages >

목차
- 1단계 : 패키지 이름을 선택한다.
- 2단계 : 디렉토리를 생성한다.
- 3단계 : 패키지 선언을 추가한다.
- 4단계 : 접근자를 추가한다.
- 5단계 : 소스를 컴파일한다.
- 6단계 : 클래스를 사용한다.

현재까지의 테스트는 아마 한 디렉토리내에서 이루어졌을 것이다. 그럼 다른 디렉토리에 있는 클래스는 어떻게 사용해야 할까? 정답은 바로 패키지이다. 패키지를 이용한 개선된 소스코드 관리 방식을 이용해보자.

패키지는 코드들을 기능적으로 분류하여 그룹화 시킨다. 패키지는 또한 정의된 클래스에 고유한 네임스페이스(namespace)를 제공한다. 아래 6단계를 통해 Address 클래스를 패키지화 시켜보자.


- 1단계 : 패키지 이름을 선택한다.
코드를 수정하기전, 먼저 패키지 이름을 무엇으로 할지 정해야 한다. Address 라는 클래스는 애초에 주소록이라는 애플리케이션의 일부로 포함시키기 위해 만들었었다. 그래서 Address 클래스에 addressbook 이라는 패키지 이름을 사용하기로 해보자.


- 2단계 : 디렉토리를 생성한다.
이번 단계에선 addressbook이라는 이름의 디렉토리를 생성해보자. 이 디렉토리에는 addressbook 패키지에 속하는 클래스들은 모두 저장되게 될 것이다. 이 디렉토리의 위치는 원하는 곳에 생성하면 된다. 


- 3단계 : 패키지 선언을 추가한다.
자, 이제 Address.fx 소스에 패키지 이름을 추가해보자. 패키지는 package라는 키워드 다음에 패키지 이름을 적게 되며, 소스의 제일 첫번째 줄에 넣어주면 된다.

package addressbook;

class Address {
     var street: String;
     var city: String;
     var state: String;
     var zip: String;
}


- 4단계 : 접근자를 추가한다.
이제 클래스에 접근자를 추가하자. 클래스를  public으로 선언하도록 해보자.

package addressbook;

public class Address {
     public var street: String;
     public var city: String;
     public var state: String;
     public var zip: String;
}

이 public 키워드에 대해서는 다음 레슨에서 배울것이며, 일단 다른 클래스에서의 모든 접근을 허용한다고만 알아두도록 하자.


- 5단계 : 소스를 컴파일한다.
addressbook 디렉토리로 이동하여 javafxc Address.fx 라고 입력하면 컴파일 된다. 컴파일이 완료되면 해당 디렉토리에 .class 파일들이 생성되었을 것이다.


- 6단계 : 클래스를 사용한다.
패키지화된 클래스를 사용하는 방식은 두가지 이다. 첫번째 방식은 "패키지명.클래스명" 과 같이 사용한다. 이름이 좀 길어진다는 단점이 있다. 두번째는 미리 import 라는 키워드를 통해 addressbook.Address 라는 "패키지명.클래스명"을 입력하여 해당 패키지의 클래스를 사용하겠다고 명시한후 클래스명인 Address 만으로 그 사용이 가능하다.

// Approach #1

addressbook.Address {
     street: "1 Main Street";
     city: "Santa Clara";
     state: "CA";
     zip: "95050";
}

// Approach #2
import addressbook.Address;

Address {
     street: "1 Main Street";
     city: "Santa Clara";
     state: "CA";
     zip: "95050";
}

이 예제를 packagetest.fx 라는 이름으로 저장하고 컴파일해보자. 별다른 에러없이 컴파일이 끝난다면 패키지를 잘 찾은 것이다.

반응형
<출처 : http://javaora.tistory.com/entry/Learning-the-JavaFX-Script-Programming-Language-Lesson-9-Writing-Your-Own-Classes >

목차
- Customer 예제
- 다른 클래스로부터의 상속

- Customer 예제
Lesson 2를 통해 객체(object)를 사용하는 방법에 대해 배웠었다. 그런데 먼저 .class 파일을 다운로드 받길 요청했고, 컴파일러를 가지고 Address와 Customer 객체를 생성하는 방법에 대해 알게 되었다. 아래 예제를 통해 빠뜨린 클래스의 정의 방법에 대해 알아보자.

def customer = Customer {
     firstName: "John";
     lastName: "Doe";
     phoneNum: "(408) 555-1212"
     address: Address {
          street: "1 Main Street";
          city: "Santa Clara";
          state: "CA";
          zip: "95050";
     }
}

customer.printName();
customer.printPhoneNum();
customer.printAddress();

class Address {
     var street: String;
     var city: String;
     var state: String;
     var zip: String;
}

class Customer {
     var firstName: String;
     var lastName: String;
     var phoneNum: String;
     var address: Address;

    function printName() {
        println("Name: {firstName} {lastName}");
    }

    function printPhoneNum(){
        println("Phone: {phoneNum}");
    }

    function printAddress(){
        println("Street: {address.street}");
        println("City: {address.city}");
        println("State: {address.state}");
        println("Zip: {address.zip}");
    }
}

위 예제는 변수와 함수에 대해 배울때 봐왔던 코드와 비슷하다. Address 클래스에는 street, city, state, zip 등의 인스턴스 변수들이 모두 String 타입으로 선언되어 있고, Customer 클래스에는 몇개의 인스턴스 변수와 그 값을 출력하는 변수들이 더해져 있다. 이 변수와 함수들은 클래스 내부에 선언되어져 있기 때문에 이들이 생성되는 곳 어디에서든 그 사용이 가능하다.


- 다른 클래스로부터의 상속

또한 클래스는 다른 클래스로부터 변수와 함수등을 상속받아 사용할 수 있다. 예를 들면, 은행에서 계좌에 저축하고 금액 확인하는 것은 생각해보자. 각 계좌는 계좌번호와 저축된 금액이 있다. 이 계좌에서 남은 금액을 조회하거나, 추가 예금을 하거나, 돈을 찾을 수도 있다. 이것을 변수와 함수를 가진 Account라는 클래스로 만들어 보자.

abstract class Account {

     var accountNum: Integer;
     var balance: Number;

     function getBalance(): Number {
          return balance;
     }

     function deposit(amount: Number): Void {
          balance += amount;
     }

     function withdraw(amount: Number): Void {
          balance -= amount;
     }
}

abstract 라는 키워드를 주목하자. 이 키워드의 의미는 Account 객체를 직접적으로 생성 할 수 없다는 뜻이다.(이렇게 생성한 목적은 오직 계좌에 저축을 하거나 저축금액을 확인하기 위함이다.)

accountNum 과 balance 라는 변수는 각각 계좌번호와 저축금액을 저장하게 된다. 남은 함수들은 각각 계좌에 남은 금액을 조회하거나(getBalance()), 저축을 하거나(deposit()), 출금을 하는(withdraw()) 기능만을 제공한다.

SavingsAccount 라는 클래스를 정의하자. 이 클래스는 extends 라는 키워드를 통해 변수와 함수를 상속 받는다.

class SavingsAccount extends Account {

     var minBalance = 100.00;
     var penalty = 5.00;

     function checkMinBalance() : Void {
          if(balance < minBalance){
               balance -= penalty;
          }
     }
}

SavingAccount 클래스는 Account의 서브 클래스로서 자동적으로 Account의 인스턴스 변수와 함수의 내용이 포함되며 사용 할 수 있다. 위 예에서 보듯 상속받는 클래스는 상속받는 변수와 함수외에 추가적으로 자신의 변수와 함수를 생성 할 수 있다.

Account 클래스를 상속받는 또 다른 클래스 CheckingAccount 라는 클래스를 정의해보자

class CheckingAccount extends Account {

     var hasOverDraftProtection: Boolean;

     override function withdraw(amount: Number) : Void {
          if(balance-amount<0 and hasOverDraftProtection){

               // code to borrow money from an overdraft account would go here

          } else {
               balance -= amount; // may result in negative account balance!
          }
     }
}

위 예제는 출금시 현재 저축액보다 더 많은 금액을 출금 할 수 없도록 막아 놓았다. 특이한 점은 withdraw라는 함수는 Account에 존재하는 함수로서 이를 재정의(override) 했다. 이렇게 재정의 되는 함수는 앞에 override라는 키워드를 사용하게 된다.
반응형
<출처 : http://javaora.tistory.com/entry/Learning-the-JavaFX-Script-Programming-Language-Lesson-8-Data-Binding-and-Triggers >


목차

- 바인딩이란?(Binding Overview)
- 바인딩과 객체(Binding and Object)
- 바인딩과 함수(Binding and Function)
- 바인딩과 시퀀스(Binding with Sequence)
- Raplace Trigger


* 바이딩이란?

bind 라는 키워드는 바인드 구문의 값인 목적변수의 값(value of target value)과 연관시키는 작업이다. 바인드 구문은 몇몇 기본 형(type)이나 객체, 함수의 결과 값 혹은 구문의 결과값으로 사용될 수 있다. 일단 말로서 설명을 하려면 약간 복잡 하지만 자바에서의 값을 참조하는 것과 동일하다고 보면 된다. 다음 설명등을 통해 좀 더 명확한 개념을 잡을 수 있을 것이다.


* 바인딩과 객체

먼저 아래 예를 먼저 보자.

var x = 0;
def y = bind x;
x = 1;
println(y); // y 는 1
x = 47;
println(y); // y 는 47

위 예에서 변수 x를 선언하고, y를 선언해 x를 바인드 시킨다.  그리고 x 값을 바꿔보자 그리고 y값을 프린트 한다. 그럼 바뀐 x 값이 출력된다. 이는 바운드된 변수인 y의 값은 x값에 따라 자동으로 업데이트 되어 새로운 값으로 바뀌게 된다.

여기서 이전 레슨에 대한 기억을 상기해보자. 우리는 예전 Lesson 2를 통해 def는 일반적으로 값을 할당한 후 상수 처럼 사용되는 변수라 설명한 바 있다. 실제로 def 로 선언된 변수의 값을 바꾸면 컴파일시 에러가 떨어진다. 하지만 bind 라는 키워드를 이용한 바인딩의 결과 그 값이 변경이 가능해진 것이다.

그럼 이를 객체에 적용해보자.

var myStreet = "1 Main Street";
var myCity = "Santa Clara";
var myState = "CA";
var myZip = "95050";

def address = bind Address {
     street: myStreet;
     city: myCity;
     state: myState;
     zip: myZip;
};

println("address.street == {address.street}");
myStreet = "100 Maple Street";
println("address.street == {address.street}");

결과는 다음과 같다.

address.street == 1 Main Street
address.street == 100 Maple Street

코드에서 보듯 address 라는 객체의 street은 myStreet이라는 변수를 참조하게 되고, 결과적으로 myStreet의 값이 바뀜에 따라 address 객체의 내부 변수인 street 변수의 값이 바뀌게 되었다.

위 예에서는 클래스인 Address를 바인딩 하였으나 그 하부의 인스턴스 변수에 까지 영향을 미침을 알 수 있다.
위 코드는 아래와 같은 방식들로도 표현 가능하다.

def address = bind Address {
     street: bind myStreet;
     city: bind myCity;
     state: bind myState;
     zip: bind myZip;
};
def address = Address {
     street: bind myStreet;
     city: bind myCity;
     state: bind myState;
     zip: bind myZip;
};


* 바인딩과 함수

이전 레슨에서 간단하게 함수에 대해 배운적이 있다. 이번에 배워야 할 부분이 더 있다. 바로 바운드 함수(bound function)와 비바운드 함수(non-bound function) 이다.

아래 함수를 보자. 아래 함수는 x, y 좌표를 받아 Point 라는 객체를 리턴한다.

var scale = 1.0;
bound function makePoint(xPos : Number, yPos : Number) : Point {
     Point {
          x: xPos * scale
          y: yPos * scale
     }
}
class Point {
     var x : Number;
     var y : Number;
}

여기서 makePoint 가 바운드 함수 이다. 바운드 함수는 function 키워드 앞에 bound라 표시한다. 이때 bound와 앞서 배운 bind를 다르다. 혼동해선 안된다.

이제 위 코드를 활용한 예를 보자.

var scale = 1.0;

bound function makePoint(xPos : Number, yPos : Number) : Point {
     Point {
          x: xPos * scale
          y: yPos * scale
     }
}

class Point {
     var x : Number;
     var y : Number;
}

var myX = 3.0;
var myY = 3.0;
def pt = bind makePoint(myX, myY);
println(pt.x);

myX = 10.0;
println(pt.x);

scale = 2.0;
println(pt.x);

위 코드의 결과는 아래와 같다.

3.0
10.0
20.0

위 결과를 보면 바운드 함수의 결과값인 Point를 바인딩한 객체 pt는 인자(argument)인 myX 값의 변화에 따라 그 인스턴스 변수 값이 달라짐을 알수 있다. 또한 scale 변수값의 변경에도 영향을 받음을 알 수 있다.

그럼 비바운드 함수 경우에는 어떨까? 위 makePoint 함수에서 앞에 bound라는 키워드를 제거하고 다시 테스트 해보면 결과는 다음과 같아진다.

3.0
10.0
10.0

scale 변수값의 변경에는 영향을 받지 않음을 알 수 있다. 비바운드 함수의 경우 함수의 인자값에 변경이 가해질때만 함수의 재호출(re-invoke)가 일어난다. scale 변수는 함수의 인자값이 아닌 함수의 외부 변수라 함수에 영향을 미치지 않음이다. 즉, 이말은 다음과 같다. 바운드 함수의 경우엔 함수의 인자나 함수에서 사용되어지는 변수의 바인드 되어지는 변수에 어떤 값의 변경이 이루어지면 내부적으로 함수의 재호출이 일어나 값이 바뀌게 되는 것이다.


* 바인딩과 시퀀스

bind는 for문에서도 사용 할 수 있다. 이를 보기전에 먼저 두개의 시퀀스를 정의하고 그 결과를 봐보자.

var seq1 = [1..10];
def seq2 = bind for (item in seq1) item*2;
printSeqs();

function printSeqs() {
     println("First Sequence:");
     for (i in seq1){println(i);}
     println("Second Sequence:");
     for (i in seq2){println(i);}
}

seq1에는 1부터 10까지 10개의 아이템(item)이 있고, seq2에도 10개의 아이템이 있다. seq2의 아이템들은 seq1의 아이템에 각각 2를 곱한 값이다. 결과는 다음과 같다.

First Sequence:
1
2
3
4
5
6
7
8
9
10
Second Sequence:
2
4
6
8
10
12
14
16
18
20

위 코드를 보면 seq1과 seq2 는 bind 키워드를 통해 바인딩 된 상태이다.

그럼 이런 의문이 든다. "만약 seq1의 모든 아이템이 바뀌거나 혹은 몇몇 아이템이 바뀐다면 seq2에 영향을 미칠까?" 한번 테스트 해보자. seq1의 제일 뒤에 11이란 값을 인서트 해보자. 그리고 seq2를 프린트 해봄으로서 확인해보자.

var seq1 = [1..10];
def seq2 = bind for (item in seq1) item*2;
insert 11 into seq1;
printSeqs();

function printSeqs() {
     println("First Sequence:");
     for (i in seq1){println(i);}
     println("Second Sequence:");
     for (i in seq2){println(i);}
}

결과는 다음과 같다.

First Sequence:
1
2
3
4
5
6
7
8
9
10
11
Second Sequence:
2
4
6
8
10
12
14
16
18
20
22

결과는 seq1의 끝에 11을 인서트 함으로서 seq2의 앞에 10개의 아이템에는 아무런 영향을 미치지 않고, seq2 끝에 22라는 값이 추가 되었음을 알 수 있다.


* Replace Trigger

Replace Trigger는 임의의 코드 블록으로 변수 뒤에 붙어 변수의 값이 바뀌었을때 실행된다. 아래의 코드가 기본 문법을 보여준다. password 라는 변수를 정의하고, 위에 replace trigger를 붙인다. password의 값이 변경되면 trigger를 통해 이 임의의 코드 블록이 실행된다.

var password = "foo" on replace oldValue {
     println("\nALERT! Password has changed!");
     println("Old Value: {oldValue}");
     println("New Value: {password}");
};

password = "bar";

결과는 다음과 같다.

ALERT! Password has changed!
Old Value: 
New Value: foo

ALERT! Password has changed!
Old Value: foo
New Value: bar
위 예제에서는 trigger가 두번 사용된다. 처음엔 password 변수가 초기화 되고 "foo"라는 값이 할당되었을 때 그리고 다시 "bar"라는 값이 할당되었을 때이다. 위 코드에서 사용된 oldValue라는 변수는 trigger가 호출되면 이전 값이 저장된다. 변수명은 여기선 임의로 oldValue라고 하였으나 정해진 것은 아니다.(사용자가 임의로 변경 가능하다.)
반응형
<출처 : http://javaora.tistory.com/entry/Learning-the-JavaFX-Script-Programming-Language-Lesson-7-Expressions >

목차
- 블록구문(Block Expression)
- if문(The if Expression)
- Range구문(Range Expression)
- for문(The for Expression)
- while문(The while Expression)
- break & continue구문(The break and continue Expression)
- 예외처리구문(The throw, try, catch and finally Expression)


* 블록구문

블록구문은 중괄호로 둘러싸인 선언문과 구문들의 집합이다. 블록구문의 값은 가장 마지막 구문의 값이다. 만일 블록구문에 어떠한 (var나 def 같은) 구문도 포함되어 있지 않다면, 그 블록구문은 Void 타입니다. 

var nums = [5, 7, 3, 9];
var total = {
var sum = 0; 
for (a in nums) { 
sum += a // num의 값을 모두 더한다.
}; 
sum; // 합계인 sum을 리턴한다.
println("Total is {total}.");

Total is 24.

위 예제에서 total 다음에 나오는 중괄호 부분이 블럭구문이다.


* if문

if문은 그동안 자바나 다른 언어를 통해 한번쯤 보았을 익숙한 구문이다. if 다음에 조건식을 입력하고 이것이 참일때 아래 구문이 실행된다. 첫번째 조건이 참이 아닐 경우 다른조건을 줄때는 else-if 를, 조건이외의 경우에는 else 를 사용하는 방식 또한 동일하다.

def age = 8;
var ticketPrice;
if (age < 5 ) {
     ticketPrice = 0; // age가 5보다 작을 경우
} else if (age < 12 or age > 65) {
     ticketPrice = 5;  // age가 12보다 작거나 65보다 큰 경우
} else {
     ticketPrice = 10;  // 위 조건을 제외한 모든 경우
}
println("Age: {age} Ticket Price: {ticketPrice} dollars.");

Age: 8 Ticket Price: 5 dollars.

위 코드는 아래와 같이 간결한 조건의 표현으로 정리 될 수 있다.

ticketPrice = if (age < 5) 0 else if (age > 5 and age < 12) 5 else 10;


* Range문

시퀀스 레슨에서 연이어지는 숫자의 시퀀스는 짧은 방식으로 표기하는 것을 배웠었다.

var num = [0..5];

기술적으로 말하자면, 위 [0..5] 와 같은 방식을 Range문 이라 한다. 위 코드에선 숫자사이의 간격(interval)은 1 이다, 하지만 step 이라는 키워드를 사용하여 이를 다른 간격만큼 증가하도록 조절 할 수 있다. 가령 1 에서 10 사이의 홀수(odd number)인 시퀀스를 만드는 방법은 아래와 같다.

var nums = [1..10 step 2];
println(nums);

출력한 결과는 다음과 같다.

[ 1, 3, 5, 7, 9 ]

감소하는 range 문을 만들기 위해선 첫번째 값이 두번째 값보다 커야 하며 step 키워드 다음에 나오는 간격(interval)은 음수(negative)여야 한다.

var nums = [10..1 step -1];
println(nums);

결과는 다음과 같다.

[ 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 ]

만일 감소하는 방식의 range문을 사용하면서 간격값으로 음수를 사용하지 않으면 컴파일시 다음과 같은 에러가 발생한다.

D:\workspace>javafxc range.fx
range.fx:1: warning: empty sequence range literal, probably not what you meant.
var nums = [10..1 step 1];
^
1 warning

D:\workspace>javafx range
[ ]

만일 step 키워드 와 증감값(interval)을 정확히 사용하지 않는다면 위와 같이 빈 시퀀스(empty sequence)를 받게 될것이다.


* for문

또 다른 시퀀스와 관련된 표현으로는 for문이 있다. for문은 시퀀스 아이템을 통한 루프 기능을 제공한다.

var days = ["Mon","Tue","Wed","Thu","Fri","Sat","Sun"];

for (day in days) {
     println(day);
}

결과는 다음과 같다.

Mon
Tue
Wed
Thu
Fri
Sat
Sun

위 코드에서 보듯이 for문은 for로 시작하며, 조건의 내용을 살펴보면 days는 시퀀스의 이름을 day 는 시퀀스 만큼 돌아가는 루프에서 현재 아이텀을 의미한다.

여기서 day 변수는 따로이 선언할 필요는 없으며, 그 범위는 해당 루프로 한정된다.

위 예에서 for문은 별도의 리턴 값이 없었지만, for문은 시퀀스를 리턴시킬 수 있다. 아래의 두가지 예를 보면 하나의 시퀀스를 통해 또 다른 시퀀스를 만들어 내는 것을 볼 수 있다.

// 결과 시퀀스는 본래 시퀀스 값의 제곱값들로 이루어진 시퀀스를 리턴받는다.
var squares = for (i in [1..10]) i*i;

// 결과 시퀀스는 ["MON", "TUE", "WED", and so on...] 와 같이 대문자로 이루어진다.
var capitalDays = for (day in days) day.toUpperCase();

위 예제에서 사용된 toUpperCase() 는  String 객체의 함수로 API 문서를 통해 확인 해볼 수 있다.


* while문

또 다른 루프문으로는 while문이 있다. while문은 for와는 달리 시퀀스의 아이템들로 작동하지 않으며, 주어진 조건이 false 일때 까지 루프를 돈다. while문의 리턴 타입은 Void 이다.


var count = 0;
while (count < 10) {
    println("count == {count}");
    count++;
}

결과는 아래와 같다.

count == 0
count == 1
count == 2
count == 3
count == 4
count == 5
count == 6
count == 7
count == 8
count == 9

위 예제 코드를 보면 변수 count 를 선언하고 값으로 0을 넣은 후 조건이 만족하지 않을 때까지 루프가 돌게 된다. 해당 조건은 count가 10보다 작은 동안이며, 값이 true인 경우 count의 값을 프린트하고  count 값을 1 증가 시킨다. 그리고 다시 조건을 확인하고 그 값이 false 일 때까지 반복한다.


* break 와 continue구문

break 와 continue구문은 루프문들과 관련이 있다. 이 두 구문들은 루프의 반복작업에 영향을 미친다. break 는 전체 루프를 종료시키고, 그에 반해 continue 는 이번 루프만(이번 반복작업만) 건너 뛰고 진행된다. break 와 continue 의 리턴 타입은 Void 이다.

for (i in [0..10]) {
     if (i > 5) { // i 가 5보다 크면 for문 전체를 종료한다.
          break;
     }

     if (i mod 2 == 0) { // i 가 짝수이면 건너 뛴다.
          continue;
     }

     println(i); // 위 continue문을 통해 결과적으로 5이하의 홀수만 프린트 한다.
}

결과는 아래와 같다.

if (i mod 2 == 0) {
continue;
}



* 예외처리구문(The throw, try, catch and finally Expression)

프로그램 구동 중에 어떤 문제가 발생하는 경우 우리는 이를 Exception이라 한다.(일반적인 의미의 Error라 보면 된다.) 가령 프로그램상에서 특정 파일을 찾아 읽는 코드가 있는데 해당 path에 해당 파일이 존재하지 않는 경우 Exception이 발생된다.

아래 예는 함수에서 Exception을 던지는 것을 정의하는 모습니다.

import java.lang.Exception;

foo();

println("The script is now executing as expected... ");

function foo() {
     var somethingWeird = false;

     if(somethingWeird){
          throw new Exception("Something weird just happened!");
     } else {
          println("We made it through the function.");
     }
}

위 스크립트를 실행하면 아래와 같은 결과가 나온다.

We made it through the function.
The script is now executing as expected...


하지만 somethingWeird 값이 true 로 바뀐다면 다음과 같은 Exception이 발생할 것이며, 충돌로 프로그램에 악영향을 끼친다.

Exception in thread "main" java.lang.Exception: Something weird just happened!
at exceptions.foo(exceptions.fx:10)
at exceptions.javafx$run$(exceptions.fx:3)


이와 같은 충돌을 방지하여면 foo() 함수가 호출되는 부분에 try/catch 구문으로 감싸주는 것이 필요하다.

try {
     foo();
} catch (e: Exception) {
     println("{e.getMessage()} (but we caught it)");
}

이렇게 try/catch문을 사용하면 해당 함수 호출시 Exception이 발생하면 catch 부분에서 이를 잡아내서 그 내부 구문을 실행한다. 이제 프로그램을 다시 실행하면 아래와 같은 결과가 나온다.

Something weird just happened! (but we caught it)
The script is now executing as expected...


finally 구문은 try/catch 다음에 꼭 실행되는 부분으로 이부분을 통해 자원해제등을 수행하거나 그외 필요한 마무리 작업을 기술한다.

try {
     foo();
} catch (e: Exception) {
     println("{e.getMessage()} (but we caught it)");
} finally {
     println("We are now in the finally expression...");
}

결과는 다음과 같다.

Something weird just happened! (but we caught it)
We are now in the finally expression...
The script is now executing as expected...

반응형
<출처 : http://javaora.tistory.com/entry/Learning-the-JavaFX-Script-Programming-Language-Lesson-6-Operators >

연산자(Operator)는 한/두개의 피연산자로 특정 연산작업을 수행하고 결과를 되돌리는(return) 특별한 기호다. 

목차
- 할당 연산자(Assignment Operators)
- 수리 연산자(Arithmetic Operators)
- 단항 연산자(Unary Operators)
- 비교 & 관계 연산자(Equality and Relational Operators)
- 조건 연산자(Conditional Operators)
- 형 비교 연산자(Type Comparison Operators)


* 할당 연산자

할당연산자 "=" 은 앞으로 보게될 가장 일반적으로 사용되어지는 연산자이다. 이 연산자는 연산자 좌측 변수에 우측 피연산자의 값을 할당한다.

result = num1 + num2;
days = ["Mon","Tue","Wed","Thu","Fri"];


* 수리 연산자

수리연산자는 4칙연산(+, -, *, /)을 수행하며, "mod" 연산자는 나눗셈을 하고 남은 값(remainder)을 결과로 리턴한다.

var result = 1 + 2; // result is now 3
println(result);

result = result - 1; // result is now 2
println(result);

result = result * 2; // result is now 4
println(result);

result = result / 2; // result is now 2
println(result);

result = result + 8; // result is now 10
println(result);

result = result mod 7; // result is now 3
println(result);

더불어 산술연산자와 할당연산자를 합친 복합할당자(compound assignment) 를 사용 할 수도 있다. 예를들면, result += 1; 은 result = result + 1 과 동일한 result 에 1을 더한 값을 다시 result 에 할당하는 작업을 수행한다. 단 "mod" 연산자는 복합할당자 형식으로 사용 할 수 없다. 예를 들어, result 를 2로 나눈 나머지 값을 다시 result 에 할당하기 위해선 result = result mod 2; 와 같이 작성해야만 한다.

var result = 0;
result += 1;
println(result); // result is now 1

result -= 1;
println(result); // result is now 0

result = 2;
result *= 5; // result is now 10
println(result);

result /= 2; // result is now 5
println(result);


* 단항 연산자

대부분의 연산자는 두개의 피연산자를 필요로 한다. 단항연산자는 하나의 피연산자를 사용하여 값을 증가(incrementing)와 감소(decrementing) 시키거나 음수(negaring a number)화 시키거나 boolean 값을 역으로 바꾸는 연산작업을 수행한다.

-   : Unary minus operator; negates a number
++  : Increment operator; increments a value by 1
--  : Decrement operator; decrements a value by 1
not : Logical complement operator; inverts the value of a boolean

var result = 1; // result is now 1

result--;  // result is now 0
println(result);

result++; // result is now 1
println(result);

result = -result; // result is now -1
println(result);

var success = false;
println(success); // false
println(not success); // true

증가/감소 연산자는 피연산자의 전처리(before, prefix)와 후처리(after, postfix) 적용이 가능하다. result++; 와 ++result; 는 둘 다 result의 값을 1 증가시키는 역할을 한다. 전처리(++result)의 경우 값을 사용하는 시점에서 이미 1을 증가시킨 상태임에 비해 후처리(result++)의 경우 값을 사용하는 시점에서는 원래 증가전 값을 사용하며 사용한후 이를 1 증가시킨다. 설명은 복잡해 보이지만 실제 예를 보면 간단하게 이해 할 수 있다.

var result = 3;
result++;
println(result); // result is now 4
++result;
println(result); // result is now 5
println(++result); // result is now 6
println(result++); // this still prints prints 6!
println(result); // but the result is now 7


* 비교 & 관계 연산자

비교 관계 연산자는 하나의 연산자와 다른 연산자를 비교하여 값의 산술적 크기가 큰지, 작은지, 같은지, 같지 않은지를 비교하며, 비산술적인 형식(예를 들면 String)의 값이 같은지 같지않은지를 비교한다.

==	equal to
!=	not equal to
>	greater than
>=	greater than or equal to
<	less than
<=	less than or equal to
 
예를 보자.

def num1 = 1;
def num2 = 2;
def str1 = "A";
def str2 = "A";
def str3 = "B";

println(num1 == num2); // prints false
println(num1 != num2); // prints true
println(num1 > num2);  // prints false
println(num1 >= num2); // prints false
println(num1 < num2);  // prints true
println(num1 <= num2); // prints true

println(str1 == str2); // prints true
println(str1 != str2); // prints false
println(str1 == str3); // prints false
println(str1 != str3); // prints true


* 조건 연산자

조건 "and"와 조건 "or" 연산자는 주어진 2개의 boolean 표현의 조건연산을 수행한다. "and" 연산자는 "and" 연산자 좌측과 우측의 조건이 모두 true 일때만 수행되며, "or" 연산자는 좌측이든 우측이든 둘중 하나가 true 이면 수행된다. 

def username = "foo";
def password = "bar";

if ((username == "foo") and (password == "bar")) {
     println("Test 1: username AND password are correct");
}

if ((username == "") and (password == "bar")) {
     println("Test 2: username AND password is correct");
}

if ((username == "foo") or (password == "bar")) {
     println("Test 3: username OR password is correct");
}

if ((username == "") or (password == "bar")) {
     println("Test 4: username OR password is correct");
}

결과는 다음과 같다.

Test 1: username AND password are correct
Test 3: username OR password is correct
Test 4: username OR password is correct


* 형 비교 연산자

"instance of" 연산자는 객체의 특정타입을 비교한다. 이를 통해 특정 객체가 어떤 클래스를 통해 인스턴스화 되었는지 알수있다(비교 할 수 있다.)

def str1="Hello";
println(str1 instanceof String); // prints true
def num = 1031;
println(num instanceof java.lang.Integer); // prints true

지금은 잘 모르겠지만 이 연산자가 차후 클래스와 상속에 대해 배운다면 유용한 것임을 알게 될것이다.

반응형
<출처 : http://javaora.tistory.com/entry/Learning-the-JavaFX-Script-Programming-Language-Lesson-5-Sequences >

목차
- 사퀀스(Sequence) 만들기
- Boolean Expression 과 시퀀스 만들기
- 시퀀스 아이템(Item) 접근하기
- 시퀀스에 아이템 인서트 하기
- 시퀀스의 아이템 삭제하기
- 시퀀스의 아이템 거꾸로 정렬 하기
- 시퀀스 비교하기
- 시퀀스 슬라이스(Slice) 사용하기

* 사퀀스(Sequence) 만들기

JavaFX에서는 앞서 배운 5개의 기본적인 데이터 타입 외에 추가적으로 시퀀스(Sequence)라 불리우는 데이터 구조(data structure) 를 제공한다. 시퀀스는 순서가 있는 List 객체(Ordered List) 를 나타내며, 시퀀스의 내부의 각각의 객체를 아이템(item) 이라 한다. 시퀀스는 대괄호([], bracket)를 사용하여 선언하며, 각각의 아이템은 콤마(,)로 구분한다.

var weekDays = ["Mon","Tue","Wed","Thu","Fri"];

선언된 시퀀스는 weekDays라 명명된 변수에 할당된다. 개별 아이템이 String 으로 선언되어 있어 우리가 "String 형식의 시퀀스(sequence of strings)"를 생성하려는 의도를 컴파일러는 알게 된다. 만일 시퀀스가 Integer들로 선언되었다면 컴파일러는"Integer 형식의 시퀀스"가 우리가 원하는 것임을 알게 된다.

물론 명시적으로 시퀀스의 아이템 형(type)을 지정 할 수도 있다. 형 지정 방식은 선언하는 변수명 다음에 콜론(:) 과 함께 원하는 데이터타입을 적어주고, 끝에는 대괄호를 사용한다.

var weekDays: String[] = ["Mon","Tue","Wed","Thu","Fri"];

시퀀스는 또한 다른 시퀀스를 포함한 중첩된 시퀀스를 만들 수도 있다.

var days = [weekDays, ["Sat","Sun"]];

이렇게 중첩된 시퀀스의 경우 컴파일러는 자동으로 내포된 시퀀스들의 아이템을 꺼내어 등가의 중첩없는(혹은 평평한, flatten) 시퀀스로 만든다.(위 와 아래의 표현은 동일하다.)

var days = ["Mon","Tue","Wed","Thu","Fri","Sat","Sun"];

또한 약식 표기법도 가능하다. 이는 순차적으로 증/감하는 숫자로 시퀀스를 구성하기 쉽게 해준다. 숫자 1 부터 100 까지로 구성되는 시퀀스를 생성하기 위한 방법은 아래와 같다.

var nums = [1..100];


* Boolean Expression 과 시퀀스 만들기

boolean expression 과 서술형 방식을 통해 기존의 시퀀스을 가지고 하위 서브셋(subset)을 구성하여 새로운 시퀀스를 선언 할 수도 있다.

아래 예에서 두번째 시퀀스는 첫번째 시퀀스에 기반을 두고 있으나 오직 숫자가 2보다 큰것만 포함하고 있다.

var nums = [1,2,3,4,5];
var numsGreaterThanTwo = nums[n | n > 2];
print();


결과 : [ 3, 4, 5 ]

var nums = [1,2,3,4,5];
var numsGreaterThanTwo = for (n in nums where n > 2) n ;
print();


결과 : [ 3, 4, 5 ]


* 시퀀스 아이템(Item) 접근하기

시퀀스의 아이템에 대한 접근은 인덱스(index)를 통해 접근하며, 인덱스의 시작은 0 이다. 사용방법은 먼저 시퀀스의 이름을 쓰고, 대괄호를 사용하며 그 안에 인덱스 값을 넣는다.

var days = ["Mon","Tue","Wed","Thu","Fri","Sat","Sun"];

println(days[0]);
println(days[1]);
println(days[2]);
println(days[3]);
println(days[4]);
println(days[5]);
println(days[6]);

그 결과는 다음과 같다.

Mon
Tue
Wed
Thu
Fri
Sat
Sun

또한 시퀀스의 크기를 구하기 위해 sizeof 키워드를 사용하며, 사용방식은 sizeof 다음에 시퀀스의 변수명을 적어준다.

var days = ["Mon","Tue","Wed","Thu","Fri","Sat","Sun"];
println(sizeof days);

위 코드의 실행결과는 7 이다.


* 시퀀스에 아이템 인서트 하기

insert 키워드는 특정 값을 into 키워드 다음의 시퀀스로 값을 끼워넣는 역할을 한다. 이때 before 와 after 키워드를 사용하여 특정 위치를 지정 할 수 있다.
Note : 사실 시퀀스는 불변형(immutable)이다. 이는 한번 생성되면 그 값이 변하지 않는다는 뜻이다. 그래서 만약 시퀀스에 insert나 delete 작업이 이루어진다면, 새로이 시퀀스를 생성하고 주어진 작업을 처리하고 이를 변수에 재 할당하는 작업이 이루어진다.
var days = ["Mon"];
insert "Tue" into days;
insert "Fri" into days;
insert "Sat" into days;
insert "Sun" into days;

print(days);

이를 실행하면 아래와 같은 결과가 나온다.

[ "Mon", "Tue", "Fri", "Sat", "Sun" ]

before 키워드를 이용하여 insert 될 element의 위치를 지정해보자.

insert "Thu" before days[2];
print(days);

이를 실행하면 아래와 같은 결과가 나온다.

[ "Mon""Tue", "Thu", "Fri""Sat""Sun" ]

after 키워드를 이용하여 insert 될 element의 위치를 지정해보자.

insert "Wed" after days[1];
print(days);

이를 실행하면 아래와 같은 결과가 나온다.

[ "Mon""Tue", "Wed", "Thu", "Fri""Sat""Sun" ]


* 시퀀스의 아이템 삭제하기

delete 와 from 키워드를 이용하여 시퀀스로 부터 쉽게 삭제가 가능하다.

delete "Sun" from  days;
print(days);

이를 실행하면 아래와 같은 결과가 나온다.

[ "Mon""Tue", "Wed", "Thu", "Fri""Sat" ]

또한 인덱스를 이용한 삭제도 가능하다.

delete days[0];
print(days);

이를 실행하면 아래와 같은 결과가 나온다.

[ "Tue", "Wed", "Thu", "Fri""Sat" ]

시퀀스의 모든 아이템을 삭제하려면 delete 키워드 다음에 시퀀스 변수명을 넣으면 된다.

delete days; // Delete all data

주의할 것은 delete 키워드는 단지 시퀀스 변수 내의 아이템을 삭제하는 것이지 변수 자체를 삭제하는 것은 아니다.

* 시퀀스의 아이템 거꾸로 정렬 하기

시퀀스의 순서를 쉽게 바꿀 수 있다.

var nums = [1..5];
reverse nums; // returns [5, 4, 3, 2, 1]


* 시퀀스 비교하기

시퀀스는 비교 가능하며, 동일한 시퀀스는 그 길이와 아이템과 순서가 동일한 것을 의미한다.

var seq1 = [1,2,3,4,5];
var seq2 = [1,2,3,4,5];
println(seq1 == seq2); // true

seq1 = [1,2,3,4,5];
seq2 = [1,2,3,4,5,6]; // 아이템이 한가지 증가했다. 순서는 바뀌지 않았다.
println(seq1 == seq2); // false

seq1 = [1,2,3,4,5];
seq2 = [1,3,2,4,5];  // 아이템은 동일하나 순서가 바뀌었다.
println(seq1 == seq2); // false


* 시퀀스 슬라이스(Slice) 사용하기

seq 키워드를 사용하여 시퀀스 내의 아이템을 분할 가능하다.

var days = ["Mon","Tue","Wed","Thu","Fri","Sat","Sun"];

var weekend = days[5..6]; // ["Sat","Sun"] 인덱스 5 와 6 사이의 아이템을 가져온다.

var weekdays = days[0..<5];//["Mon","Tue","Wed","Thu","Fri"] 인덱스 0 과 4(=5보다 작은) 사이의
                                         // 아이템을 가져온다.

weekend = days[5..]; // ["Sat","Sun"] 인덱스 5 부터 끝까지의 아이템을 가져온다.

var days2 = days[0..<]; // ["Mon","Tue","Wed","Thu","Fri","Sat"] 인덱스 0부터 마지막보다 작은 사이의
                                  // 아이템을 가져온다.(마지막은 제외)
반응형

<출처: http://javaora.tistory.com/entry/Learning-the-JavaFX-Script-Programming-Language-Lesson-4-Data-Types >

목차
- String
- Number and Integer
- Boolean
- Duration
- Void
- Null

* String

String은 홑('') 또는 겹(쌍, "") 따옴표로 선언한다.

var s1 = 'Hello';
var s2 = "Hello";

홑/겹따옴표는 대칭적으로 홑따옴표 사이에 겹따옴표를 넣거나, 반대로 겹따옴표 안에 홑따옴표를 포함시킬 수 있다. 홑 따옴표와 겹따옴표 사이의 차이는 없다.

추가적으로 String 내부에 중괄호({})를 이용하여 expression 를 사용하는 것도 가능하다.

def name = 'Joe';
var s = "Hello {name}"; // s = 'Hello Joe'

내포된 expression 은 그 자신 안에 따옴표로 둘러싸인 String을 사용 할 수 있으며, 그 String 안에 또 다른 expression 을 포함 시킬 수도 있다.

def answer = true;
var s = "The answer is {if (answer) "Yes" else "No"}"; // s = 'The answer is Yes'

런타임(runtime) 시에, 컴파일러는 answer 의 값에 따라 적절히 Yes 와 No 를 선택하게 된다.

여러개의 String을 연결하는 작업은 따옴표 내에 중괄호들을 사용하여 연결한다.

def one = "This example ";
def two = "joins two strings.";
def three = "{one}{two}"; // join string one and string two
println(three); // 'This example joins two strings.'


* Number and Integer

Number 와 Integer 는  수적인(numerical) 데이터를 나타내며, 대부분의 경우 값을 할당하면 컴파일러에서 자동으로 추론하여 올바른 형(type)을 선택한다.

def numOne = 1.0; // compiler will infer Number
def numTwo = 1; // compiler will infer Integer

물론 정확한 변수의 타입을 지정 할 수도 있다.

def numOne : Number = 1.0;
def numTwo : Integer = 1;

Number 는 부동소수점 숫자를 나타내며, Integer 는 오직 정수를 표시한다. 따라서 Number는 정밀한 부동소수점 계산시 필수적인 것이고, 일반적인 경우 대부분 Integer를 사용하면 될 것이다.


* Boolean

Boolean 형은 true 와 false 라는 두가지 값(상태)를 나타낸다. 이 형식은 어떤 애플리케이션의 특정 내부적인 상태를 나타내기에 좋으며 주로 조건문의 값을 선택할때 많이 사용된다.

var isAsleep = true;
if (isAsleep) { wakeUp(); }

위 조건문의 괄호("()")안의 값이 true 이면 중괄호({}) 내부의 함수 호출 문장이 실행된다. 조건문과 관련한 좀 더 자세한 사항은 다음시간의 Expression 편에서 알아보도록 하겠다.


* Duration

Duration 형은 고정된 단위시간(밀리초, 초, 분, 시간 등) 을 나타낸다.

5ms; // 5 milliseconds
10s; // 10 seconds
30m; // 30 minutes
1h; // 1 hour

Duration 형은 시간단위(time literals) 와 함께 표시된다. - 예를들면, 5m에서 m은 분(minute) 을 나타낸다. 시간단위는 대부분 애니메이션을 사용할 때 필요로 한다.(애니메이션과 관련된 사항은 차후 레슨에서 배우게 될것이다.)


* Void

Void는 함수의 리턴 형이 없는 것을 나타낼때 사용된다.

function printMe() : Void {
     println("I don't return anything!");
}

위 코드는 명시적으로 함수의 리턴형을 생략하는 것과 동일하다.

function printMe() { 
     println("I don't return anything!"); 
}

JavaFX의 Void 키워드는 대문자 V로 시작된다. Java의 경우 소문자 void로 사용하는데 주의해야 한다.

* Null

Null은 특수한 값(value)으로 값이 없는 상태를 나타내는 값이다. Null은 0이나 빈 String 값("")과는 다르다. Null을 0이나 빈 String 값과 비교한 결과는 false 이다.(같지 않다.)

Null 키워드는 비교하는 것은 가능하며, 일반적으로 아래와 같은 형식으로 사용된다.

function checkArg(arg1: Address) {
     if(arg1 == null) {
          println("I received a null argument.");
     } else {
          println("The argument has a value.");
     }
}

위 함수는 하나의 인자를 받아 인자의 값이 null인지를 체크하고 결과를 프린트한다.
반응형
<출처 : http://javaora.tistory.com/entry/Learning-the-JavaFX-Script-Programming-Language-Lesson-3-Using-Objects >

목차
- 객체란?
- 객체구(Object Literal) 선언
- 객체구 문법
- 인스턴스(Instance) 함수 호출

* 객체(Object)란?

객체란 무엇인가? 객체는 상태와 행동으로 구성된 개별의 소프트웨어 꾸러미이다. 간단히 말해 :

  • 객체의 상태는 자신의 변수를 나타낸다.
  • 객체의 행동은 자신의 함수를 나타낸다.
개념적으로 버튼, 체크박스, 레이블 같은 GUI 컴포넌트부터 보이지 않는 추상적 개념의 날씨 데이터, 재정 상태, 상품 등록 정보 등의 어떤것이든 객체로 만들어 낼 수 있다. 

Note : 좀 더 많은 정보를 알고 싶으면 자바 튜터리얼 의  "객체란 무엇인가" 를  보도록 하자.

* 객체구 선언(= 객체 생성, 인스턴스 생성)

JavaFX 스크립트 개발언어에서는 객체는 객체구(object literal) 로서 생성된다.(인스턴스를 생성한다. - 본 글에서 "객체를 생성한다"와 "인스턴스를 생성한다" 는 동일한 의미로 보면 된다.)


Address {
     street: "1 Main Street";
     city: "Santa Clara";
     state: "CA";
     zip: "95050";
}


위 예제는 가상의 주소록 애플리케이션에서 사용되는 Address 객체를 생성한다. 본 소스를 적당한 이름을 저장하고 컴파일 하면 다음과 같은 에러가 발생한다.
  

위 코드는 마치 자바 문법의 생성자와 비슷하며 다음과 같은 의미로 보면 된다.

new Address("1 Main Street", "Santa Clara", "CA", "95050");

그리고 위 에러는 참조해야할 Address 클래스가 존재하지 않아 발생한 문제이다.

위 Address.zip 을 다운받아 풀면 두개의 .class 파일과 하나의 AddressBook.fx 파일이 있다.(AddressBook.fx 는 위 코드와 동일한 내용이다.)

이제 다시 컴파일 해보자.


이젠 아무런 에러없이 컴파일이 될 것 이다.


* 객체구 문법 

객체구의 문법은 배우기 쉽고 직관적인 사용이 가능하다.

Address {
     street: "200 Pine Street",
     city: "San Francisco",
     state: "CA",
     zip: "94101",
}
Address {
     street: "200 Pine Street"
     city: "San Francisco"
     state: "CA"
     zip: "94101"
}
Address {
     street: "200 Pine Street",
     city: "San Francisco",
     state: "CA",
     zip: "94101",
}

위 코드에서 첫번째 단어 Address는 생성하려는 객체의 타입을 나타낸다. 중괄호 내부의 블럭은 객체의 내용을 정의하며, 변수들은 각각 객체의 멤버변수를 초기화 시킨다. 객체구를 선언할때는 인스턴스의 멤버변수를 콤마나 빈칸 또는 세미콜론으로 구분된다.

또한 아래와 같은 방식으로 생성된 객체를 변수에 할 당 할 수도 있다.

def addressOne = Address {
     street: "1 Main Street";
     city: "Santa Clara";
     state: "CA";
     zip: "95050";
}

def addressTwo = Address {
     street: "200 Pine Street";
     city: "San Francisco";
     state: "CA";
     zip: "94101";
}

또한 아래 코드와 같이 객체 내부에 다른 객체를 포함 시킬 수도 있다.

def customer = Customer {
     firstName: "John";
     lastName: "Doe";
     phoneNum: "(408) 555-1212";
     address: Address {
          street: "1 Main Street";
          city: "Santa Clara";
          state: "CA";
          zip: "95050";
     }
}

위 코드에서 Customer 객체는 원 address 라고 명명된 변수에 할당된 Address 객체를 포함한 몇가지 새로운 변수들을 가지고 있다. 내포된 객체(nested object) 구문은 일반적으로 많이 사용되는 형식이다.

아래 Customer.zip을 다운로드 받아 위에서 다운받은 Address.zip 과 같은 곳에 압축을 푼다. 그리고 위 코드를 적당한 이름으로 저장 후 컴파일 하면 제대로 컴파일 되는 것을 알 수 있다.


* 인스턴스(Instance) 함수 호출

JavaFX에는 함수(function)이 포함된  사용가능한 많은 클래스들이 있다. 일단 함수를 생성하는 방법은 다음 레슨에서 살펴볼 것이며, 이번엔 먼저 존재하는 함수를 어떻게 사용하는지에 대해 알아보자.

객체의 인스턴스 함수(instance function)는 객체명 다음에 마침표(.)를 붙이고 다음에 호출하기 원하는 함수명을 입력하여 호출한다.

def customer = Customer {
     firstName: "John";
     lastName: "Doe";
     phoneNum: "(408) 555-1212";
     address: Address {
          street: "1 Main Street";
          city: "Santa Clara";
          state: "CA";
          zip: "95050"; }
}
customer.printName();
customer.printPhoneNum();
customer.printAddress();

그 결과는 아래와 같다.

Name: John Doe
Phone: (408) 555-1212
Street: 1 Main Street
City: Santa Clara
State: CA
Zip: 95050

아마 위 코드를 보고 이런 의문을 갖게 될것이다.
"저 함수 이름은 어디서 온거지? 내가 객체속에든 인스턴스 변수나 함수명을 어떻게 한번에 알 수가 있겠어?"
만약 library나 클래스를 사용하고 싶다면, API 문서를 참고하면 된다. API 문서에는 객체들의 변수명과 함수명이 리스트업 되어 있다. API 문서를 참고하는 것만이 주어진 객체의 기능을 파악 할 수 있는 가장 확실한 방법이다.

반응형

목차

- 간단한 계산기 작성
- 스크립트 변수 선언
- 스크립트 함수 정의 및 호출
- 스크립트 함수에 인자 넘기기
- 스크립트 함수의 리턴 값
- Command-Line 인자 접근

* 간단한 계산기 작성

에디터를 열고 아래 코드를 입력한다.

def numOne = 100;
def numTwo = 2;
var result;

add();
subtract();
multiply();
divide();

function add() {
     result = numOne + numTwo;
     println("{numOne} + {numTwo} = {result}");
}

function subtract() {
     result = numOne - numTwo;
     println("{numOne} - {numTwo} = {result}");
}

function multiply() {
     result = numOne * numTwo;
     println("{numOne} * {numTwo} = {result}");
}

function divide() {
     result = numOne / numTwo;
     println("{numOne} / {numTwo} = {result}");
}



컴파일과 실행은 자바와 유사함을 알 수 있다.

* 스크립트 변수 선언

이제 calculator.fx 를 자세히 들여다 보자.(차후 이 예제를 확장 할 것이다.)

코드의 첫번째 부분은 변수의 선언으로 시작된다.

def numOne = 100;
def numTwo = 2;
var result;

스크립트의 변수 선언엔 var 와 def 키워드 두가지가 사용된다. var 는 스크립트 전체에 통해 계속적으로 새로운 값을 할당 할 때 사용하며(그야말로 진정한 변수이다.), def 는 변수 선언 처음에 값을 할당하여 상수로서 사용되어진다.

위 코드에서도 계속적으로 사용되어질 상수와 같은 값은 def 로 선언하고 초기값을 할당하였고, 매 계산시 마다 값을 저장할 result 는 var 로 선언하고 초기화하지 않음을 알 수 있다.

우리는 result 변수에 숫자만 들어간다는 타입설정을 하지 않았음에도 컴파일시 문제가 없다. 이는  컴파일러가 사용자가 작성한 코드상의 변수의 사용을 보고 그 의도를 파악하여 자동으로 타입을 계산(추측) 해낸다. 이를 형 추론(Type Inference)라 한다. 이 형 추론은 스크립트 프로그래머에게 데이터 타입 선언이라는 짐을 덜어주므로서 당신의 일을 좀 더 쉽게 해준다.

* 스크립트 함수 정의 및 호출

소스의 남은 부부은 두 숫자의 덧셈/뺄셈/곱셈/나눗셈 의 스크립트 함수를 정의한다.

function add() {
     result = numOne + numTwo;
     println("{numOne} + {numTwo} = {result}");
}
...

함수는  특정 업무를 수행하는 실행가능한 코드의 블럭이다. 이 예에서 각 함수는 4칙연산중 하나를 수행하고 그 결과를 프린트 한다. 함수로 코드를 구조화(블럭화) 하는 것은 일반적인 것으로서 이는 프로그램을 읽기 쉽고, 쓰기 쉽고, 디버그 하기 쉽게 만들어 준다. 함수의 몸체(body)부분은 중괄호(brace)를 이용해 시작과 끝을 나내 가독성을 높인다.

함수는 명시적으로 호출(invoke)되기 전까지 실행되지 않는다. 이는 스크립트상 어느 위치에서든 함수가 실행 가능하게 만든다. 함수를 호출하는 것은 함수를 선언하기 전이든 후든 큰 문제가 되지 않는다. 위 예제에서도 함수가 실제 소스코드상에 선언되기 전 호출되어지고 있다.

함수의 호출은 아래와 같다.

add();
subtract();
multiply();
divide();


* 스크립트 함수에 인자 넘기기

다음으로 이전의 calculator.fx 의 함수를 인자(arguments)를 받도록 수정 해보자. 인자는 함수를 호출하면서 함수로 넘기는 특정 값(specific value)이다. 이 수정으로 위 사칙연산 함수는 하드코딩된 고정된 값이 아닌 어떤 임의의 두 숫자라도 계산을 할 수 있게 해준다.

var result;

add(100,10);
subtract(50,5);
multiply(25,4);
divide(500,2);

function add(argOne: Integer, argTwo: Integer) {
     result = argOne + argTwo;
     println("{argOne} + {argTwo} = {result}");
}

function subtract(argOne: Integer, argTwo: Integer) {
     result = argOne - argTwo;
     println("{argOne} - {argTwo} = {result}");
}

function multiply(argOne: Integer, argTwo: Integer) {
     result = argOne * argTwo;
     println("{argOne} * {argTwo} = {result}");
}

function divide(argOne: Integer, argTwo: Integer) {
     result = argOne / argTwo;
     println("{argOne} / {argTwo} = {result}");
}

컴파일 후 재실행 한 결과는 아래와 같다.


위 코드에는 제일 위에 정의된 numOne 과 numTwo 변수가 더 이상 필요가 없어 삭제되었다. 대신 함수에 정의된 인자를 통해 두 숫자를 넘겨 받도록 수정 되었다. 각 인자는 각각의 이름과 콜론(:) 로 구분된 타입으로 나타내진다. 하나 이상의 인자는 콤마(,)로 구분한다.

* 스크립트 함수의 리턴 값

함수는 선택적으로 값을 함수를 호출한 코드로 리턴할 수 있다. 예를 들면, add 함수의 결과 값이 리턴되도록 수정 되어질 수도 있다. - Bold 표시 참고

function add(argOne: Integer, argTwo: Integer) : Integer {
     result = argOne + argTwo;
     println("{argOne} + {argTwo} = {result}");
     return result;
}

이 add 함수는 아래와 같이 사용한다.

var total;
total = add(1,300) + add(23,52);

리턴되는 값이 없는 경우 함수는 default 로 Void 를 리턴한다.

* Command-Line 인자 접근

우리는 위 코드를 command-line 인자를 받아들이도록 수정 할 수도 있다. 이를 통해 런타임시 계산되는 숫자를 입력 할 수 있다.

var result;

function run(args : String[]) {

     // Strings 을 Integers 로 변환
     def numOne = java.lang.Integer.parseInt(args[0]);
     def numTwo = java.lang.Integer.parseInt(args[1]);

     // 함수 호출 
     add(numOne,numTwo);
     subtract(numOne,numTwo);
     multiply(numOne,numTwo);
     divide(numOne,numTwo);
}

function add(argOne: Integer, argTwo: Integer) {
     result = argOne + argTwo;
     println("{argOne} + {argTwo} = {result}");
}

function subtract(argOne: Integer, argTwo: Integer) {
     result = argOne - argTwo;
     println("{argOne} - {argTwo} = {result}");
}

function multiply(argOne: Integer, argTwo: Integer) {
     result = argOne * argTwo;
     println("{argOne} * {argTwo} = {result}");
}

function divide(argOne: Integer, argTwo: Integer) {
     result = argOne / argTwo;
     println("{argOne} / {argTwo} = {result}");
}

위 소스를 보면 눈에 띄는 점이 있는데 바로 run() 함수 블럭의 추가이다. 이 run() 함수는 일반적인 함수와는 다른 특별한 함수로 모든 스크립트의 시작점이 된다.(Java의 main() 함수와 같은 역할을 한다.) run() 함수는 모든 command-line 인자를 args 라는 String 형의 시퀀스(Sequence)에 저장해두고 있다.(Sequence는 순서가 있는 리스트로서 다른 언어의 배열과 비슷하다. 시퀀스는 세부적으로 Expressions 와 Operators로 나뉜다. - Lesson 6과 7 에서 상세히 설명함.)

이 스크립트를 실행하려면, 계산하려는 두 숫자를 런타임시 입력해야 한다. 실행 결과는 아래와 같다.


이번 소스에도 numOne과 numTwo 변수가 있다. 하지만 이번에 변수의 Scope는 run() 메소드 안으로 제한된다. 이 calculator 함수는 숫자를 인자로 받으므로 String 형식의 command-line 인자를 Integer로 컨버팅 한다. 이때 Java API의 도움을 받아 형변환을 수행한다.
run() 함수는 명시적으로 입력하지 않더라도 컴파일러가 컴파일시 자동으로 run() 함수를 생성한다. 마치 자바의 생성자 처럼 말이다. 따라서 컴파일된 bytecode 상에선 run() 함수를 명시한것과 명시하지 않을 것 사이의 큰 차이는 없다.

+ Recent posts