20190827のJavaに関する記事は7件です。

【Java】varを使うべき場合、使うべきではない場合

概要

Java10から、varを使ったローカル変数の型推論が使えるようになりました。便利である反面、使い方を誤ると可読性が落ちる恐れがあるため、どのような場合に使うべきなのか考えてみました。(Java10が出たのが1年以上前なので今更な話ですが)

動作環境

Java10以降

そもそもvarって何?

ローカル変数の宣言時、変数の型を指定する代わりに「var」と書くことで、型の指定を省略することができます。いわゆる型推論です。

// 従来の書き方
String s = "hogehoge";
// varを使った書き方
var s = "hogehoge";
// StringクラスのメソッドであるtoUpperCase()を呼べる(変数sはString型と認識されている)
System.out.println(s.toUpperCase());  // => HOGEHOGE

何がうれしいの?

どんなに型名が長くても「var」と書くだけで済むため、書くのが楽になります。また、冗長な情報がなくなってすっきりするので、読むのも楽になるはず。

// 従来の書き方
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd");
// varを使った書き方
var dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd");

DateTimeFormatterというそこそこ長い名前が2回も出現していたのが1回になり、書くのも読むのも少し楽だと思います。

動的型付けではない!

varを使っても静的型付けであることに変わりはありません。変数に型がなくなるわけではなく、ただ変数の定義時に型が自動的に決定されるだけです。一度varで定義された変数に、別の型の値を再代入することはできません。(コンパイルエラーになる)1

// 右辺が文字列リテラルであるため、変数sは自動的にString型になる
var s = "hogehoge";
// sはStringなので、intなど別の型の値は代入できない
s = 123;
//  エラー:
//  不適合な型: intをjava.lang.Stringに変換できません:
//  s = 123;
//      ^-^

また、varで変数の定義時に右辺から型を一意に特定できない場合もコンパイルエラーです。

// nullでは何の型なのかわからないためコンパイルエラー
var v = null;
//  エラー:
//  ローカル変数vの型を推論できません
//    (変数初期化子は'null'です)
//  var v = null;
//  ^-----------^

JavaScriptのvar等とは全く違いますので、誤解なきように。

varを使うべきかどうか

varとは何なのかわかったと思います。(たぶん)
ここから、掲題の通りどんな場合にvarを使うべきか、使わないべきかについて考えていきます。

varを使うべき場合

一言でいえば、右辺を見れば一目で何の型かわかるような場合です。
具体的には下記のような場合が想定されます。

右辺でインスタンス化している場合

// 右辺に型名がそのまま書いてあるので、何の型か一見してわかる
var date = new Date();

var scanner = new Scanner(System.in);

var list = new ArrayList<String>();

右辺がリテラルである場合

// 右辺が文字列リテラルであるため、String型であると一見してわかる
var s = "hogehoge";
// 右辺が整数リテラルであるため、int型であると一見してわかる
var n = 123;

右辺で戻り値の型を容易に推測できるメソッドを呼び出している場合

// メソッド名が型名を含んでいるのでわかる
var br = Files.newBufferedReader(Paths.get("filename"));

// 現在日時の取得だが、LocalDateTimeのstaticメソッドなのでLocalDateTime型だろうと推測できる
var date = LocalDateTime.now();

// Calendarのインスタンスを取得するわけなので当然Calendar型だろうと推測できる
var cal = Calendar.getInstance();

varを使うべきではない場合

varを使うべき場合の逆ですが、一言でいえば右辺を見ても戻り値の型を正確には推測できないメソッドを呼び出している場合です。

// Date#getTime()
// 日付を表す型はいろいろあるので「Time」だけではわからない(実際はlong型)
var time = date.getTime();

// Path#getFileName()
// ファイル名だからStringかな?(実際はPath型)
var filename = path.getFileName();

// File#getAbsolutePath()
// PathとあるからこれもPath型?(実際はString型)
var path = file.getAbsolutePath();

// Files#copy(Path, OutputStream)
// なんか受け取ってるけど何なのかわからない。数値っぽいからint?(実際はコピーしたバイト数を表すlong型の値)
var size = Files.copy(path, out);

おまけ

無名クラスと組み合わせて使うことで、特定のメソッド内でのみ有効なメソッドを定義することができます。本題とは関係ないのですが、便利かもしれないので書いておきます。

Main.java
public class Main {
    public static void main(String[] args) {
        // func.capitalize() で呼び出せる
        // func はローカル変数なので、このメソッドの外からは呼べない
        var func = new Object() {
            private String capitalize(String s) {
                return s.substring(0, 1).toUpperCase() + s.substring(1, s.length()).toLowerCase();
            }
        };

        System.out.println(func.capitalize("tokyo")); // => Tokyo
        System.out.println(func.capitalize("TOKYO")); // => Tokyo
    }
}

まとめ

  • 右辺を見て型が容易にわかる場合はvarを使ってOK
  • 右辺を見ても型がわからない、または型を勘違いするような場合はvarを使わないほうがいい
  • おまけとして、varを使えばメソッド内でメソッドを定義するっぽいことができる

参考リンク


  1. もちろん、サブクラスなど代入互換性のある型の場合はこの限りではありません。 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Spring Framework bean definition XML: custom tag

At first

In document of Spring Framework, there is an example of define bean by using XML element "<util:list>".
https://docs.spring.io/spring/docs/5.0.x/spring-framework-reference/core.html#xsd-schemas-util-list

<!-- creates a java.util.List instance with the supplied values -->
<util:list id="emails">
    <value>pechorin@hero.org</value>
    <value>raskolnikov@slums.org</value>
    <value>stavrogin@gov.org</value>
    <value>porfiry@gov.org</value>
</util:list>

The code above has same function of below code.

<!-- creates a java.util.List instance with values loaded from the supplied 'sourceList' -->
<bean id="emails" class="org.springframework.beans.factory.config.ListFactoryBean">
    <property name="sourceList">
        <list>
            <value>pechorin@hero.org</value>
            <value>raskolnikov@slums.org</value>
            <value>stavrogin@gov.org</value>
            <value>porfiry@gov.org</value>
        </list>
    </property>
</bean>

I am interest in this magic, so I start to look into source code.

The turth of magic

First, I tried to find out classes which implement interface org.springframework.beans.factory.xmlBean.DefinitionParser in Eclipse. A lot of classes came out. And one of class org.springframework.beans.factory.xml.UtilNamespaceHandler (In Java archive spring-beans-3.2.3.RELEASE.jar) I felt suspect. So I read the code of this

org.springframework.beans.factory.xml.UtilNamespaceHandler
...
    public void init() {
        registerBeanDefinitionParser("constant", new ConstantBeanDefinitionParser());
        registerBeanDefinitionParser("property-path", new PropertyPathBeanDefinitionParser());
        registerBeanDefinitionParser("list", new ListBeanDefinitionParser());
        registerBeanDefinitionParser("set", new SetBeanDefinitionParser());
        registerBeanDefinitionParser("map", new MapBeanDefinitionParser());
        registerBeanDefinitionParser("properties", new PropertiesBeanDefinitionParser());
    }
...

May be I found out the class which handle XML element "list". In the same Java archive, I tried to look into "/META-INF/spring.handlers"

http\://www.springframework.org/schema/c=org.springframework.beans.factory.xml.SimpleConstructorNamespaceHandler
http\://www.springframework.org/schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler
http\://www.springframework.org/schema/util=org.springframework.beans.factory.xml.UtilNamespaceHandler

Bingo! Elements in "util" namespace is really handle by class org.springframework.beans.factory.xml.UtilNamespaceHandler. Return to method init(). XML element "list" is handle by class ListBeanDefinitionParser(inner class of org.springframework.beans.factory.xml.UtilNamespaceHandler).

org.springframework.beans.factory.xml.UtilNamespaceHandler
...
    private static class ListBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {

        @Override
        protected Class getBeanClass(Element element) {
            return ListFactoryBean.class;
        }

        @Override
        protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
            String listClass = element.getAttribute("list-class");
            List parsedList = parserContext.getDelegate().parseListElement(element, builder.getRawBeanDefinition());
            builder.addPropertyValue("sourceList", parsedList);
            if (StringUtils.hasText(listClass)) {
                builder.addPropertyValue("targetListClass", listClass);
            }
            String scope = element.getAttribute(SCOPE_ATTRIBUTE);
            if (StringUtils.hasLength(scope)) {
                builder.setScope(scope);
            }
        }
    }
...

Method getBeanClass() is for getting List bean factory class org.springframework.beans.factory.config.ListFactoryBean. And method doParse() is for reading XML element properties and child element.

Reference

https://docs.spring.io/spring/docs/5.0.x/spring-framework-reference/core.html#xml-custom

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Gradle でサブディレクトリを含むマルチプロジェクトを構成する

概要

サブディレクトリによる階層構造がある場合で、Gradleのマルチプロジェクトを構築する方法になります。
下記、記事を参考にさせていただきました。
https://qiita.com/shiena/items/371fe817c8fb6be2bb1e

環境

  • Java 8
  • Gradle 5.6
  • IntelliJ IDEA Ultimate 2019.2
  • Windows10

コードサンプル

サンプルコードを Github にアップしています。
https://github.com/tYoshiyuki/java-gradle-multiproject

構成例

以下のような構成を想定しています。

プロジェクト名 説明
master ルートプロジェクト
common/main-lib 共通ライブラリ その1
common/sub-lib 共通ライブラリ その2
app/api 個別のアプリケーションサンプル
app/web 個別のアプリケーションサンプル
app/batch 個別のアプリケーションサンプル

設定方法

root フォルダにてプロジェクト全体の Gradle 設定を行います。
includeFlat にて同階層にある common, app フォルダを指定します。
その後、include にてサブディレクトリのプロジェクトを指定します。

settings.gradle
rootProject.name = 'root'
includeFlat 'common', 'app'
include 'common:main-lib', 'common:sub-lib'
include 'app:web', 'app:api', 'app:batch'

各アプリケーションのプロジェクトから、共通ライブラリを参照します。

build.gradle
project('app:web') {
    dependencies {
        implementation project(':common:main-lib')
        implementation project(':common:sub-lib')
    }
}

IntelliJ IDEAよりGradleプロジェクトとしてインポートすると、各プロジェクトが認識されていることが確認できます。

image.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

音ゲー作成までの道のり2

前回からの進捗

前回からは左右に振ると音が出るようになりました!...それだけですm(_ _)m
左右に振れば音が出るようにするのなど完成させる前の私は簡単だと思っていましたが、とてつもなくめんどくさい...
なぜか振ってもいないのに音が出たり、振っていなくても音が出ないようになったと思えば振っても音が出ないし、多重反応を起こすし、いろいろ工夫し、やっと左右に振ると音が出るようになりました!下記にて示します。

ソースコード

MainActivity.java
public class MainActivity extends AppCompatActivity implements SensorEventListener {
    ImageView Image1, Image2, Image3, Image4;
    int i,j, Delay;
    float X_Data, Y_Data, Z_Data;

    private Timer timer1;
    private CountUpTimerTask timerTask1;
    private Handler handler1 = new Handler();
    private Runnable runnable;
    private final Handler handler = new Handler();

    private SensorManager sensorManager;
    private TextView X_Data_TextView , Y_Data_TextView , Z_Data_TextView;

    SoundPool soundPool;
    int mp3_1,mp3_x;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        sensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
        X_Data_TextView = findViewById(R.id.X_Data_Text);
        Y_Data_TextView = findViewById(R.id.Y_Data_Text);
        Z_Data_TextView = findViewById(R.id.Z_Data_Text);
        StartCyclicHandler();

        Image1 = findViewById(R.id.image1);
        Image2 = findViewById(R.id.image2);
        Image3 = findViewById(R.id.image3);
        Image4 = findViewById(R.id.image4);
        Button startButton = findViewById(R.id.strat);
        Button endButton = findViewById(R.id.End);
        EditText et = findViewById(R.id.BpM);

        Image1.setImageResource(R.drawable.en);
        Image2.setImageResource(R.drawable.en);
        Image3.setImageResource(R.drawable.en);
        Image4.setImageResource(R.drawable.en);

        //EditTextに数字しか入力できないようにする
        et.setInputType(InputType.TYPE_CLASS_NUMBER);

        //入力された値をint型に変換
        String bpm = ((EditText) findViewById(R.id.BpM)).getText().toString();
        int BPM = Integer.parseInt((bpm));

        //効果音付けるのに必要なやつ
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
            soundPool = new SoundPool(5, AudioManager.STREAM_MUSIC, 0);
        } else {
            AudioAttributes attr = new AudioAttributes.Builder()
                    .setUsage(AudioAttributes.USAGE_MEDIA)
                    .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                    .build();
            soundPool = new SoundPool.Builder()
                    .setAudioAttributes(attr)
                    .setMaxStreams(5)
                    .build();
        }



        mp3_1 = soundPool.load(this, R.raw.pop, 1);
        mp3_x = soundPool.load(this,R.raw.feed1,1 );

        //STARTButton
        startButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                i = 0;

                String bpm = ((EditText) findViewById(R.id.BpM)).getText().toString();
                int BPM = Integer.parseInt((bpm));
                Delay = 60000 / BPM;

                if (null != timer1) {
                    timer1.cancel();
                    timer1 = null;
                }

                Image1.setImageResource(R.drawable.en);
                Image2.setImageResource(R.drawable.en);
                Image3.setImageResource(R.drawable.en);
                Image4.setImageResource(R.drawable.en);

                timer1 = new Timer();

                timerTask1 = new CountUpTimerTask();

                timer1.schedule(timerTask1, 0, Delay);
            }

        });

        //ENDButton
        endButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (null != timer1) {
                    timer1.cancel();
                    timer1 = null;
                }


                Image1.setImageResource(R.drawable.en);
                Image2.setImageResource(R.drawable.en);
                Image3.setImageResource(R.drawable.en);
                Image4.setImageResource(R.drawable.en);
            }
        });
    }

    class CountUpTimerTask extends TimerTask {
        @Override
        public void run() {
            handler1.post(new Runnable() {
                @Override
                public void run() {
                    if (i == 0) {
                        Image1.setImageResource(R.drawable.eng);
                        Image4.setImageResource(R.drawable.en);
                        soundPool.play(mp3_1, 2, 2, 0, 0, 1f);
                        i++;
                    } else if (i == 1) {
                        Image1.setImageResource(R.drawable.en);
                        Image2.setImageResource(R.drawable.eng);
                        soundPool.play(mp3_1, 2, 2, 0, 0, 1f);
                        i++;
                    } else if (i == 2) {
                        Image2.setImageResource(R.drawable.en);
                        Image3.setImageResource(R.drawable.eng);
                        soundPool.play(mp3_1, 2, 2, 0, 0, 1f);
                        i++;
                    } else {
                        Image3.setImageResource(R.drawable.en);
                        Image4.setImageResource(R.drawable.eng);
                        soundPool.play(mp3_1, 2, 2, 0, 0, 1f);
                        i = 0;
                    }
                }
            });

        }
    }


    @Override protected void onResume() {
        super.onResume();
        // Event Listener登録
        Sensor accel = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        sensorManager.registerListener(this, accel, SensorManager.SENSOR_DELAY_NORMAL);
    }

    @Override protected void onPause() {
        super.onPause();
        // Event Listener登録解除
        sensorManager.unregisterListener(this);
        StoptCyclicHandler();
    }



    @Override public void onSensorChanged(SensorEvent event) {
        if(event.sensor.getType() == Sensor.TYPE_ACCELEROMETER){
            X_Data = (500+(event.values[0]*25));
            Y_Data = (500+(event.values[1]*25));
            Z_Data = (500+(event.values[2]*25));
        }
    }

    protected void StartCyclicHandler(){
        runnable = new Runnable() {
            @Override
            public void run() {

                X_Data_TextView.setText(String.format("%.3f",X_Data));
                Y_Data_TextView.setText(String.format("%.3f",Y_Data));
                Z_Data_TextView.setText(String.format("%.3f",Z_Data));

                if(X_Data>800){
                    soundPool.play(mp3_x, 2, 2, 0, 0, 1f);
                }
                else if (X_Data<150){
                    soundPool.play(mp3_x, 2, 2, 0, 0, 1f);
                }
            }

                handler.postDelayed(this,200); //200msの遅延
            }
        };
        handler.post(runnable);
    }

    protected void StoptCyclicHandler(){
        handler.removeCallbacks(runnable);
    }

    @Override public void onAccuracyChanged(Sensor sensor, int accuracy) {
    }

}

今回追加したのは、加速度センサを扱う部分と左右に振ると音が出るようにする部分です(そのまんま)。
左右に振る部分で工夫した点は、遅延を入れることです。
遅延を入れることにより、音が重複して鳴らなくなりました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

(メモ)Java for文

for文での繰り返し処理

ループ処理・繰り返し処理と呼ばれる処理をまとめます。
Javaでは次の3つが用意されています。

  • for文
  • while文
  • do文(do while)

記述式

  • for(初期値の設定;継続条件;値の操作){処理}

  • while(条件){処理}

  • do{処理}while(条件);

コードを書いてみよう

数字を入力し、その個数だけ*を表示するプログラム

import java.util.Scanner;

class PutKome {

  public static void main(String[] args) {
    Scanner stdIn = new Scanner(System.in);

    System.out.print("何個お米を表示しますか:");
    int n = stdIn.nextInt();

    for (int i = 0; i < n; i++) {
      System.out.print('*');
    }
    System.out.println();
  }
}

【結果】

何個*を表示しますか:5
*****

解説

  1. nの入力値を受け取る
  2. [初期値]iに0を代入する
  3. [継続条件]iがn未満?
  4. Yes:*を出力、[操作]i+1…Noになるまで繰り返す/No:改行

1566876503855.jpg

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

eclipceでプロジェクトが表示されないとき

eclipceでプロジェクトが表示されないときの対処法

状況
CVSレポジトリーでチェックアウト済みであり、前日の夜までは開けていたのだが、次の日にプロジェクトが真っ白になっていた。
プロジェクトのディレクトリ自体は存在するけどワークスペースを再開してもでない、、、

対処法
左上のファイルから、「インポート」
次に「一般」を押して「既存のプロジェクトをワークスペースへ」を選択
自身のプロジェクトのルートディレクトリを指定して「プロジェクトをワークスペースにコピーにチェック」を選択し、「終了」。

これでプロジェクトが復活してビルドされるはずです!!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Web APIって便利すぎない???

Web APIとはなんぞや?

どうも、ぽんぽこです。

前回の記事で大まかにAPIとは何か。イメージは掴んでいただけましたか?
今回は、APIはAPIでも「Web API」について少しでもご理解していただけたら幸いです。

ご指摘やこうしたらもっと読みやすいんじゃない?などご意見お待ちしております!!

では、本編へ。

そもそもWeb APIとは?

1つの機能に特化したプログラムで共有可能なものやソフトウェアの機能を共有する仕組みを
Web APIと言うそうだ。

とにかくめちゃめちゃ便利!!!
だからWeb APIを使っている企業は世界中に恐ろしいほどあります。

前回の記事で記述した通り、
Amazon、Facebook、Twitter、Google、ぐるなび、楽天などだ。

ではなぜわざわざWeb APIを使用するのだろうか?

答えは単純明快。

  • 異なるプログラミング言語で開発されたアプリケーション間を連携させることが可能。
  • Webを経由しているので最新情報を取得できる。
  • バグ修正などの運用コストも必要がない。
  • 多くのWeb APIは無料で公開されている。

デメリットが思いつかないぐらいのメリットの多さ。

実際にデメリットを調べてみたが全然見当たらない。。。
(デメリットを知っている方がいらっしゃったら教えてください。)

メリットばっかりですごい!!!
使いたい人は全員使えるの?

これらのメリットの前では、使用不可避。

Web APIの使用に必ず必要なものがある。

それは、、、

「APIキー」と「APIシークレットキー」だ!!!

これは、APIを提供しているサイトに登録した際に発行されるので
これを使用してWeb APIを使用しなければならない。

APIキーとはそのアプリのIDで
APIシークレットキーとはパスワードだ!

以上の2つがなければなぜできないの???と一晩中悩むことになりますよ。。。

最後に、すごくわかりやすい図があったので引用させていただきます。
スクリーンショット 2019-08-27 4.55.55.png

では、最後まで読んで頂きありがとうございます!!

また次回!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む