LoginSignup
4
7

More than 5 years have passed since last update.

Gradle で RxJava を動かす

Last updated at Posted at 2015-12-29

この冬こそ関数型プログラミングにデビューしようと思い立ち、RxJava なるものを動かしてみました。

Gradle で Hello World

まず、gradle で Java プログラムを動かす方法を確認します。
適当なディレクトリ (gradle-hello とか) に Hello.java があるとします。

Hello.java
public class Hello {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}

ふつーはこうしますよね。

$ javac Hello.java
$ java Hello
Hello, World!
$ 

簡単なプログラムならこれでもいいけど、ライブラリの指定とか面倒。
ってわけで、gradle の出番です。

Gradle で Java プログラムを動かす

これを gradle で動かすには、まず build.gradle が必要です。

build.gradle
apply plugin: 'java'
apply plugin: 'application'

mainClassName = 'Hello'

次に、src/main/java ディレクトリを作って、その中にソースを置きます。

$ mkdir -p src/main/java
$ mv Hello.java src/main/java
$ tree
.
├── build.gradle
└── src
    └── main
        └── java
            └── Hello.java

3 directories, 2 files
$ 

あとは gradle におまかせ。

$ gradle run
:compileJava
:processResources UP-TO-DATE
:classes
:run
Hello, World!

BUILD SUCCESSFUL

Total time: 5.164 secs

This build could be faster, please consider using the Gradle Daemon: https://docs.gradle.org/2.10/userguide/gradle_daemon.html
$ 

-q を付けるとおとなしくなります。

$ gradle -q run
Hello, World!
$ 

コンパイルしたものは build ディレクトリに生成されてます。

$ tree
.
├── build
│   ├── classes
│   │   └── main
│   │       └── Hello.class
│   ├── dependency-cache
│   └── tmp
│       └── compileJava
├── build.gradle
└── src
    └── main
        └── java
            └── Hello.java

9 directories, 3 files
$ 

ちなみに、build.gradle で指定した java というプラグインは Java のソースをコンパイルして jar などを作ってくれるものですが、実行するには application というプラグインも必要になります。
これだから Java ってやつは…。

Gradle でコマンドライン引数

プログラムが動いたら、コマンドライン引数も扱いたいですよね。
ついでに、後でテストを書くことも考慮して、Hello.java を次のように書き換えます。

Hello.java
public class Hello {
    public String message(String name) {
        return "Hello, " + name + "!";
    }

    public static void main(String[] args) {
        Hello hello = new Hello();
        if (args.length == 0) {
            System.out.println(hello.message("World"));
        } else {
            for (String name : args) {
                System.out.println(hello.message(name));
            }
        }
    }
}

引数を付けて実行してみましょう。

$ gradle -q run Earth Moon Sun

FAILURE: Build failed with an exception.

* What went wrong:
Task 'Earth' not found in root project 'gradle-hello'.

* Try:
Run gradle tasks to get a list of available tasks. Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
$ 

残念ながら、コマンドライン引数は build.gradle で指定する必要があります。

build.gradle
apply plugin: 'java'
apply plugin: 'application'

mainClassName = 'Hello'
run.args = ['Earth', 'Moon', 'Sun']
$ gradle -q run
Hello, Earth!
Hello, Moon!
Hello, Sun!
$ 

または、-P オプションで指定する方法もあるけど、複数指定できないし、省略したら怒られるので、面倒。

build.gradle
apply plugin: 'java'
apply plugin: 'application'

mainClassName = 'Hello'
run.args = [project.args]
$ gradle -q run -Pargs="Earth Moon Sun"
Hello, Earth Moon Sun!
$ gradle -q run

FAILURE: Build failed with an exception.

* Where:
Build file '/Users/hoshi/gradle-hello/build.gradle' line: 5

* What went wrong:
A problem occurred evaluating root project 'gradle-hello'.
> Could not find property 'args' on root project 'gradle-hello'.

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
$ 

main なんか実行してないで、ちゃんとテストを書けってことなんでしょうね。
これだから Java ってやつは…。

Gradle で JUnit

テストコードを書く場合は、まず build.gradle を次のように書き換えます。

build.gradle
apply plugin: 'java'

repositories {
    mavenCentral()
}

dependencies {
    testCompile 'junit:junit:4.12'
}

次に、src/test/java ディレクトリを作って、テストクラスを置きます。

$ mkdir -p src/test/java
$ vi src/test/java/HelloTest.java
...
$ tree src
src
├── main
│   └── java
│       └── Hello.java
└── test
    └── java
        └── HelloTest.java

4 directories, 2 files
$ 
HelloTest.java
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertEquals;

public class HelloTest {
    Hello hello;

    @Before
    public void setup() {
        hello = new Hello();
    }

    @Test
    public void testMessage() {
        assertEquals("Hello, World!", hello.message("World"));
    }
}

あとは gradle におまかせ。

$ gradle test
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:compileTestJava
:processTestResources UP-TO-DATE
:testClasses
:test

BUILD SUCCESSFUL

Total time: 5.918 secs

This build could be faster, please consider using the Gradle Daemon: https://docs.gradle.org/2.10/userguide/gradle_daemon.html
$ 

build/reports/tests/index.html をブラウザで開くと、テスト結果のレポートが見れます。

Gradle で RxJava

それでは RxJava を試してみましょう。
ObserverパターンとHelloWorldからはじめるRxJava を参考にしました。

HelloRx1.java
import rx.Observable;

public class HelloRx1 {
    public static void main(String[] args) {
        String[] names = { "Ben", "George" };
        Observable.from(names).subscribe(name -> System.out.println("Hello, " + name + "!"));
    }
}

build.gradle もちょっと書き換えて、コマンドラインから実行するクラスを指定できるようにします。

build.gradle
apply plugin: 'java'
apply plugin: 'application'

mainClassName = project.main

repositories {
    mavenCentral()
}

dependencies {
    compile 'io.reactivex:rxjava:1.1.0'
}
$ gradle -q run -Pmain=HelloRx1
Hello, Ben!
Hello, George!
$ 

もひとつ。

HelloRx2.java
import rx.Observable;
import rx.Observer;

public class HelloRx2 {
    public static void main(String[] args) {
        String[] names = { "Ben", "", "George" };

        Observable.from(names).subscribe(new Observer<String>() {
            @Override
            public void onNext(String name) {
                if (name.isEmpty()) {
                    throw new RuntimeException("Empty name");
                }
                System.out.println("Hello, " + name + "!");
            }

            @Override
            public void onCompleted() {
                System.out.println("Completed.");
            }

            @Override
            public void onError(Throwable e) {
                System.out.println("Error: " + e);
            }
        });
    }
}
$ gradle -q run -Pmain=HelloRx2
Hello, Ben!
Error: java.lang.RuntimeException: Empty name
$ 

さらに高度な例。(Learning Reactive Programming with Java 8 より拝借。)

SumRx.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import rx.Observable;
import rx.Subscriber;
import rx.observables.ConnectableObservable;

public class SumRx {
    public static void main(String[] args) {
        System.out.println("start");
        ConnectableObservable<String> input = from(System.in).publish();
        Observable<Double> a = varStream("a", input);
        a.subscribe(v -> System.out.println("a = " + v));
        Observable<Double> b = varStream("b", input);
        b.subscribe(v -> System.out.println("b = " + v));
        Observable.combineLatest(a, b, (x, y) -> x + y).subscribe(sum -> System.out.println("sum = " + sum));
        input.connect();
    }

    public static Observable<String> from(InputStream stream) {
        return from(new BufferedReader(new InputStreamReader(stream)));
    }

    public static Observable<String> from(BufferedReader reader) {
        return Observable.create(new Observable.OnSubscribe<String>() {
            @Override
            public void call(Subscriber<? super String> subscriber) {
                if (subscriber.isUnsubscribed()) { return; }
                try {
                    while (!subscriber.isUnsubscribed()) {
                        String line = reader.readLine();
                        if (line == null || line.equals("exit")) { break; }
                        subscriber.onNext(line);
                    }
                } catch (IOException e) {
                    subscriber.onError(e);
                }
                if (!subscriber.isUnsubscribed()) {
                    subscriber.onCompleted();
                }
            }
        });
    }

    public static Observable<Double> varStream(String varName, Observable<String> input) {
        return input
                .map(str -> str.split(":"))
                .filter(array -> array.length == 2 && array[0].equals(varName))
                .map(array -> Double.parseDouble(array[1]));
    }
}

gradle で動かしたプログラムから標準入力を読み込むために、以下を追加。

build.gradle
run {
    standardInput = System.in
}
$ gradle -q run -Pmain=ch01.Sum
start
a:1
a = 1.0
a:2
a = 2.0
b:3
b = 3.0
sum = 5.0
b:4
b = 4.0
sum = 6.0
c:5
a:6
a = 6.0
sum = 10.0
b:7
b = 7.0
sum = 13.0
exit
$ 

あとは各自いろいろ試せばいいと思います。

4
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
7