いろいろガンガンいこうぜ。体も大事に

普段はJavaでAndroidアプリ開発しているプログラマーのブログです。

Javaの列挙型について(1)

Javaの列挙型について。


    public static final int DIRECTION_NORTH = 0;
    public static final int DIRECTION_EAST = 1;
    public static final int DIRECTION_SOUTH = 2;
    public static final int DIRECTION_WEST = 3;

    private static void showDirection(int direction) {
        switch (direction) {
            case DIRECTION_NORTH:
                System.out.println("北");
                break;
            case DIRECTION_EAST:
                System.out.println("東");
                break;
            case DIRECTION_WEST:
                System.out.println("西");
                break;
            case DIRECTION_SOUTH:
                System.out.println("南");
                break;
            default:
                break;
        }
    }

    public static void main(String[] args) {
        showDirection(DIRECTION_NORTH);
    }


方角として定数を定義。
その方角を引数して取る(ことを想定した)メソッドと,
それをつかったmain関数。


しかし,こんなコードも書けてしまう。

    public static void main(String[] args) {
        showDirection(0);
        showDirection(-1);
    }


showDirectionメソッドは引数として方角を取ることを想定している。
しかし,実際はint型の引数だから,方角の定数以外も引数にできてしまう。
そのため,北の入力としてDIRECTION_NORTHでなく0を入れることもできる。
また,0,1,2,3,以外の値も入力にできる。



良くない。



実際には上のようなソースコードを書くことは無いと思うけれど。
結果的にそうなってしまうパターンもあるのでは。(あるかな?)




そこで,列挙型を使ってみた。

Direction.java

package com.rmstar.enumsample;

public enum Direction {
    NORTH,
    EAST,
    SOUTH,
    WEST,
}


Main.java

package com.rmstar.enumsample;

public class Main {

    private static void showDirection(Direction direction) {
        switch (direction) {
            case NORTH:
                System.out.println("北");
                break;
            case EAST:
                System.out.println("東");
                break;
            case WEST:
                System.out.println("西");
                break;
            case SOUTH:
                System.out.println("南");
                break;
            default:
                break;
        }
    }

    public static void main(String[] args) {
        showDirection(Direction.NORTH);
    }
}


列挙体を使えば,showDirectionの中に0や-1みたいな想定外の入力が入ることも無い。




Javaの 列挙型は他の言語と違ってクラス。
次はこの当りを確認したいと思います。





それでは,また会えることを祈りつつ。

C#で継承とかポリモーフィズムとか(2) 抽象クラスとかインターフェースとか

抽象クラスを使ってみた。

abstract class AbstractSuper
{
	public void Process()
	{
		Prepare();
		Execute();
	}

	abstract protected void Prepare();
	abstract protected void Execute();
}

class ImplA : AbstractSuper
{
	protected override void Prepare ()
	{
		Console.WriteLine ("Prepare ImplA");
	}

	protected override void Execute ()
	{
		Console.WriteLine ("Execute ImplA");
	}
}

class ImplB : AbstractSuper
{
	protected override void Prepare ()
	{
		Console.WriteLine ("Prepare ImplB");
	}

	protected override void Execute ()
	{
		Console.WriteLine ("Execute ImplB");
	}
}

class MainClass
{
	public static void Main (string[] args)
	{
		new ImplA().Process();
		Console.WriteLine ();
		new ImplB().Process();
	}
}



特に違うところは無いのかな?



Prepare ImplA
Execute ImplA

Prepare ImplB
Execute ImplB





インターフェースを使ってみた。






interface IHogeHoge
{
	void Hello();
	void HogeHoge();
}

interface IFugaFuga
{
	void Hello();
	void FugaFuga();
}

class Impl : IHogeHoge, IFugaFuga
{
	public void Hello ()
	{
		Console.WriteLine ("Hello");
	}

	public void FugaFuga ()
	{
		Console.WriteLine ("FugaFuga");
	}

	public void HogeHoge ()
	{
		Console.WriteLine ("HogeHoge");
	}
}

class MainClass
{
	public static void Main (string[] args)
	{
		Impl impl = new Impl();
		impl.FugaFuga();
		impl.HogeHoge();
		impl.Hello();
	}
}

override書かないんだ。
実行結果は,


FugaFuga
HogeHoge
Hello


次みたいなこともできる。


interface IHogeHoge
{
	void Hello();
	void HogeHoge();
}

interface IFugaFuga
{
	void Hello();
	void FugaFuga();
}	

class Impl : IHogeHoge, IFugaFuga
{
	void IHogeHoge.Hello ()
	{
		Console.WriteLine ("HogeHoge Hello");
	}

	void IFugaFuga.Hello ()
	{
		Console.WriteLine ("FugaFuga Hello");
	}

	public void FugaFuga ()
	{
		Console.WriteLine ("FugaFuga");
	}

	public void HogeHoge ()
	{
		Console.WriteLine ("HogeHoge");
	}
}


class MainClass
{
	public static void Main (string[] args)
	{
		Impl impl = new Impl();
		impl.FugaFuga();
		impl.HogeHoge();
		((IHogeHoge)impl).Hello();
		((IFugaFuga)impl).Hello();
	}
}

なるほど。
要キャスト。

C#で継承とかポリモーフィズムとか(1) 仮想関数?

class Super
{
	public void Hello ()
	{
		Console.WriteLine ("Hello, this is Super class.");
	}
}

class Sub : Super
{
	public void Hello ()
	{
		Console.WriteLine ("Hello, this is Sub class.");
	}
}

class MainClass
{
	public static void Main (string[] args)
	{
		Super super = new Super();
		super.Hello();
			
		Sub sub = new Sub();
		sub.Hello();
			
		Super subInSuper = new Sub();
		subInSuper.Hello();
	}
}


C#で継承。
よくある例。だと思う。
実行結果は,こうなりました。

Hello, this is Super class.
Hello, this is Sub class.
Hello, this is Super class.

3行目が,期待していたのと違う。
Superクラスのメソッドが呼び出されていて,
オーバーライドされていない?


オーバーライド"される"側にvirtual
オーバーライド"する"側にoverride
が必要らしい。

書き換えると,

class Super
{
	public  virtual void Hello ()
	{
		Console.WriteLine ("Hello, this is Super class.");
	}
}

class Sub : Super
{
	public override void Hello ()
	{
		Console.WriteLine ("Hello, this is Sub class.");
	}
}

class MainClass
{
	public static void Main (string[] args)
	{
		Super super = new Super();
		super.Hello();
			
		Sub sub = new Sub();
		sub.Hello();
			
		Super subInSuper = new Sub();
		subInSuper.Hello();
	}
}

ちゃんと動いた。

Hello, this is Super class.
Hello, this is Sub class.
Hello, this is Sub class.


仮想関数。
サブクラスでオーバーライドして挙動を変更することが出来る関数。
wikipedia先生が教えてくれました。


C++やC#だとvirtualって書いて仮想関数にしないと,
オーバーライドしても挙動が変わらないらしい。
ちなみにJavaは全ての関数が仮想関数らしい。


virtual書かなくて,はまることがありそうだから気をつけないと。




以上です。
それでは,また会えることを祈りつつ。

Hello Widget! (1) 最小構成のWidget

Widget


Androidを持ち始めてそろそろ1年と半年だけど,
正直しっかりとWidgetを使い始めたのは最近。


作ってみた経験が無かったので,
とりあえずHello WorldWidgetを作ってみようと思います。


Hello worldプログラムはHello Worldって表示するだけで,何もしない。
だから今回つくるWidgetもとりあえず,
ホーム画面に置けて,Hello Widget Worldって書いてある。
それだけの最小公正なWidgetを作ってみたいと思います。


Eclipseで,普通のアプリケーションを作るようにプロジェクトを作成
パッケージ名:com.rmstar.hellowidget
プロジェクト名:HelloWidget




まずは,Widgetのレイアウトを定義。

res/hello_widget_layout.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#FF777777" >

    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:layout_margin="10dip"
        android:background="#FFFFFFFF"
        android:gravity="center"
        android:text="@string/hello_world"
        android:textColor="#FF000000"
        android:textSize="12sp" />
</RelativeLayout>




widgetのホーム画面上でのサイズ,更新間隔,レイアウト指定をするファイルを作成。
res/xml/hello_widget.xml

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:initialLayout="@layout/hello_widget_layout"
    android:minHeight="72dip"
    android:minWidth="72dip"
    android:updatePeriodMillis="0" >

</appwidget-provider>


マニフェストファイルを編集。
Activityなし。
AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.rmstar.hellowidget"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="15" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <receiver
            android:name=".HelloWidgetProvider"
            android:label="@string/widget_name" >
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>

            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/hello_widget" />
        </receiver>
    </application>

</manifest>




やっと,Javaの編集。
だけど空っぽ。

src/com/rmstar/hellowidget/HelloWidgetProvider

package com.rmstar.hellowidget;

import android.appwidget.AppWidgetProvider;

public class HelloWidgetProvider extends AppWidgetProvider{

}


これと各種サイズのアイコンic_launchar.pngと,
文字列定義ファイルstrings.xmlを書き換えて,不要な物は消しました。


res/values/strings.xml

<resources>

    <string name="app_name">HelloWidget</string>
    <string name="hello_world">Hello\nwidget\nworld!</string>
    <string name="widget_name">HelloWidget</string>

</resources>


実機につなげて,Eclipseで実行するも応答なし。




えっ?
出てこない。





そりゃそうでした。
Widgetなんで。
Widget選択画面で普通にありました。
HelloWidgetが。


うっかり。




とりあえず,最小構成
HelloWorldがでるだけのWidgetはできました。




それではまた会えることを祈りつつ。

C#のプロパティを使ってみた。

class Person
{

	private string name;
	private int age;

	public Person (string name, int age)
	{
		this.name = name;
		this.age = age;
	}
	
	public string GetName ()
	{
		return name;
	}

	public int GetAge ()
	{
		return age;
	}

	public void SetAge (int age)
	{
		this.age = age;
	}
		
	public override string ToString ()
	{
		return string.Format ("[{0} : age {1}]", name, age);
	}
}

というクラスをプロパティを使って書き換えてみました。
こんなかんじですかね。


class Person
{
		
	public Person(string name, int age)
	{
		this.Name = name;
		this.Age = age;
	}

	public string Name { private set; get; }
	public int Age{ set; get; }

	public override string ToString ()
	{
		return string.Format ("[{0} : age {1}]", Name, Age);
	}
}


短い!
いいですね。これ。


ただ外からこれを使う時に,

person.GetName()

でなくて,

person.Name


とするのが,今は違和感を感じます。




考察?とか


プロパティは,
使う側から見るとインスタンス変数に見えて,
実装する側から見るとメソッドと同じように実装が出来る
だそうで。


実装が変わった時その影響範囲が広くなってしまうため,
publicなフィールド(インスタンス変数)でなく,
privateなフィールドで,それに対するアクセッサメソッドを定義するべき,
というのは,いろいろな本で言及されています。

プロパティーを使うと非常に短く,アクセッサーを実装できていいですね。


プロパティーの細かい実装も定義できるようで,以下のようにも書けるようです。

class Person
{

	private string name;

	public Person(string name, int age)
	{
		this.name = name;
		this.Age = age;
	}

	public string Name
	{
		get{
			return name;
		}
	}

	public int Age{ set; get; }

	public override string ToString ()
	{
		return string.Format ("[{0} : age {1}]", Name, Age);
	}
}

以上です。

また会えることを祈りつつ。

C#で単純なクラスを定義してみた。

名前と年齢を表すフィールドと,そのアクセッサー。
そして,toStringをオーバーライド。
これだけの単純なクラスをC#で作りたいと思います。
Javaだとこんな感じですかね。


public class Person {
    private String mName;
    private int mAge;

    public Person(String name, int age) {
        mName = name;
        mAge = age;
    }

    public String getName() {
        return mName;
    }

    public int getAge() {
        return mAge;
    }

    public void setAge(int age) {
        mAge = age;
    }

    @Override
    public String toString() {
        return String.format("[%s : age %d]", mName, mAge);
    }
}


使う側。

public static void main(String[] args) {

    Person taro = new Person("Taro", 24);
    System.out.println(taro);

    System.out.println(String.format("%s is %d years old.", taro.getName(), taro.getAge()));
    taro.setAge(taro.getAge() + 1);
    System.out.println("A year later.");
    System.out.println(String.format("%s is %d years old.", taro.getName(), taro.getAge()));

    System.out.println(taro);

}


C#で書いてみました。
こんな感じですかね。


class Person
{

	private string name;
	private int age;

	public Person (string name, int age)
	{
		this.name = name;
		this.age = age;
	}
	
	public string GetName ()
	{
		return name;
	}

	public int GetAge ()
	{
		return age;
	}

	public void SetAge (int age)
	{
		this.age = age;
	}
		
	public override string ToString ()
	{
		return string.Format ("[{0} : age {1}]", name, age);
	}
}


使う側は,

public static void Main (string[] args)
{
	Person taro = new Person ("taro", 24);
	Console.WriteLine (taro);
			
	Console.WriteLine (string.Format ("{0} is {1} years old.", taro.GetName(), taro.GetAge()));
	taro.SetAge(taro.GetAge() + 1);
	Console.WriteLine ("A year later.");
	Console.WriteLine ("{0} is {1} years old.", taro.GetName(), taro.GetAge());
			
	Console.WriteLine (taro);

}

フィールド・インスタンス変数の命名規約

Code Style Guidelines for Contributorsに沿ってJavaを書く際自分は,
privateで否staticなフィールド名には,「m」をつけています。

C#の方は,
技術評論社さんの"読みやすく効率的なコードの原則 C#ルールブック"の56ページ。
「2.12.3 public 以外のインスタンス変数はCamel形式にする」に従い,
フィールド(C#だからインスタンス変数か)Camel形式にしました。


@Overrideとoverride修飾子

Javaの方のコードの場合,@Overrideアノテーションがなくとも動作する。
しかしC#の方のToStringメソッドはoverride修飾子が無い場合,
期待した動作をせず,だたクラス名を返す挙動をしました。


ToStringメソッドはスーパークラスで定義され,
Personクラスでそれをオーバーライドしたメソッドでしょうから,
継承関係の事項を学習した時にまたそのあたりを確認したいと思います。





次はプロパティ当りをまとめたいと思います。
それでは,また会えることを祈りつつ。

C#とJavaのHello worldを見比べて。

JavaHello worldと,

package com.mrstar;

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

C#Hello world

using System;

namespace HelloCSWorld
{
	class MainClass
	{
		public static void Main (string[] args)
		{
			Console.WriteLine ("Hello World!");
		}
	}
}

見比べて思ったこと。





中括弧「{」の位置にすごい違和感が...

業務では,JavaAndroidを開発していて,
コントリビューター用のコーディングスタイルですが,
Code Style Guidelines for Contributors
http://source.android.com/source/code-style.html#use-standard-brace-style
に沿うようなスタイルで開発しています。
このスタイルでは,中括弧は改行していません。
(上記のページのBrace Style参照)


一方で,C#の手元にある参考書は全て中括弧は「{」で改行していました。
Visual StudioC#コーディング規則でも,中括弧を改行する形式で書かれていました。
http://msdn.microsoft.com/ja-jp/library/vstudio/ff926074.aspx


中括弧
違和感ありまくり。


まぁ書いているうちになれるか。



Pascal形式なメソッド名にも違和感...

これも,違和感が
「えっ,クラス?」とか思ってしまう。
これもそのうち慣れるか。



namespace

Javaのpackageに該当するのかな?
C#のnamespaceはJavaのpackageと違って,
ソースコードの物理パスとか考えなくていいんですね。
それから,ファイル名とクラス名が一致する必要も無いんですね。


下記のソースコードみたいに,
一つのファイルに,複数のnamespaceも定義出来れば,
namespace入れ子による階層化も出来れば,
.による階層化も出来るみたいですね。
でも,単一ファイルでこんなに定義はしないか。

using System;

namespace HelloCSWorld
{
	class MainClass
	{
		public static void Main (string[] args)
		{
			NameSpaceA.Util.ShowMessage();
			NameSpaceB.Util.ShowMessage();
			NameSpaceA.Sub.Util.ShowMessage();
			NameSpaceB.Sub.Util.ShowMessage();
		}
	}
}


namespace NameSpaceA
{
	class Util
	{
		public static void ShowMessage ()
		{
			Console.WriteLine("NameSpaceA.Util");
		}
	}

	namespace Sub
	{
		class Util
		{
			public static void ShowMessage ()
			{
				Console.WriteLine("NameSpaceA.Sub.Util");
			}
		}
	}
}

namespace NameSpaceB
{
	class Util
	{
		public static void ShowMessage ()
		{
			Console.WriteLine("NameSpaceB.Util");
		}
	}
}

namespace NameSpaceB.Sub
{
	class Util
	{
		public static void ShowMessage ()
		{
			Console.WriteLine("NameSpaceB.Sub.Util");
		}
	}
}


そういえば,もう何年前かは忘れたけど初めてJavaをやったころ
クラス名を変えたんだけど,
ファイル名を変え忘れて
はまったことを思い出しました。





以上です。
それでは,また会えることを祈りつつ。