Generics

C#ジェネリックのクラス図

Javaジェネリックのクラス図

C#

public class MyClass<T> { }

new MyClass<string>()

Dictionary<TKey, TValue>

public void Method<T>(T t){ }

string s = obj.Method<string>();

制約

where T: struct 値型(Nullable除外)

where T: class 参照型(クラス、インターフェイス、デリゲート、配列)

where T: new() パラメータなしのパブリック コンストラクタ(最後に指定)

where T: <baseclass name>

where T: <interface name> 複数可

where T: U

同じ名前空間内に共存できる

MyClass, MyClass<T>, MyClass<T,U>

System.Attribute の子孫クラスをジェネリックにすることはできない

オーバーロードはできる

public class MyClass {

public void Method() { }

public void Method<T>() { }

public void Method<T1,T2>() { }

}

ジェネリックなコンストラクタは宣言できない

ジェネリックなクラスの静的メンバ (フィールド・メソッド) OK

class MyClass<T> {

static MyClass() {

System.Console.WriteLine("Initialized: {0}", typeof(T)); //Tごとに出力が違う

}

}

class Program {

public static void Main() {

new MyClass<object>();

new MyClass<string>();

}

}

class MyGenericClass<T> : object where T : MyClass { }

class MyGenericClass<T>

where T : MyClass, MyInterface1, MyInterface2 { }

class MyGenericClass<T, U>

where T : MyClass

where U : T { }

メソッド

T Method<T>() where T : MyClass { }

Tは参照型でも値型でもよい

コンストラクタに関する制約ができる

void Init<T>(T[] array) where T : new() {

for (int i = 0; i < array.Length; i++)

array[i] = new T();

}

ジェネリックな型の配列

List<string>[] a = new List<string>[3]; // OK

ワイルドカードを使用できないため、型アーギュメントの汎用的な扱いが困難

インタフェースとデリゲートの変性(参照型のみ)

共変

public interface IEnumerable<out T> : IEnumerable { ... }

型 T から型 U へ暗黙的にキャスト可能ならば、

IEnumerable<T> から IEnumerable<U> に暗黙的にキャストできる

IEnumerable<string> s = new List<string>();

IEnumerable<object> o = s;

反変

public interface IComparable<in T> { ... }

型 T から型 U へ暗黙的にキャスト可能ならば、

IComparable<U> から IComparable<T> に暗黙的にキャストできる

IComparable<object> c = new List<object>();

IComparable<string> s = c;

C# 4.0 ではクラスやメソッドの型パラメータに対して変性を指定できない

共変な型パラメータはメソッドの戻り値の型としては使えるが、引数の型としては使えない

反変な型パラメータはメソッドの引数の型としては使えるが、戻り値の型としては使えない

class MyClass1<T> {

IEnumerable<T> list;

public void Copy(IList<T> list2) {

foreach (T t in list)

list2.Add(t);

}

}

MyClass1<string> o = new MyClass1<string>();

IList<object> l = new List<object>();

o.Copy(l); // 不可

class MyClass2<T> {

IList<T> list;

public void Copy(IEnumerable<T> list2) {

foreach (T t in list2)

list.Add(t);

}

}

MyClass2<object> o = new MyClass2<object>();

IEnumerable<string> l = new List<string>();

o.Copy(l); // OK

リフレクションで型の違いはチェックできる

is や as で型を判別したり、typeofで型の Type オブジェクトを取得したりできる

Type GetListTypeArgument<T>(object obj) {

if (obj is List<T>) {

return typeof(T);

} else {

return null;

}

}

コンパイル時と実行時ともにジェネリックの機能を使用できる

★配列について

//一次元

int[] array = new int[5];

int[] array = new int[]{1,2,3,4,5};

int[] array = {1,2,3,4,5};

//二次元

int[,] array = new int[2,3]; //2行3列

int[,] array = { {1,2,3}, {4,5,6} };

//Jagged Array(配列の配列)

int[][] array = new int[2][];

array[0] = new int[]{1,2,3,4};

array[1] = new int[]{1,2,3};

或いは

int[][] array = new int[][]

{

new int[]{1,2,3,4},

new int[]{1,2,3}

};

★Listについて

●初期化

// 方法1

List<string> list1 = new List<string>(); //Capacity=0

List<string> list1 = new List<string>(10); //Capacity=10

list1.Add("Item1");

list1.Add("Item2");

// 方法2

string[] items = { "Item1", "Item2" };

List<string> list2 = new List<string>(items);

// 方法3(推薦)

List<int> list3 = new List<int> { 0, 1, 2 };

List<string> list4 = new List<string> { "Item1", "Item2" }; //Capacity=2

●操作

list.Insert(index, "xxx"); //index <= Capacity - 1

list.Remove("xxx"); //複数の場合にindexが小さい方を削除

int n = list.BinarySearch("1"); //検索(含まれる場合に0を返す)

List<int> listA = new List<int> {1,2,3};

List<int> listB = new List<int> {3,4,5};

listA.AddRange(listB); //合併

List<int> result = listA.Union(listB).ToList<int>(); //重複を除く

List<int> result = listA.Concat(listB).ToList<int>(); //重複を残る

配列も同様

int[] i = new int[]{1,2,3};

int[] j = new int[]{3,4,5};

List<int> list = new List<int>();

list.AddRange(i); //合併

int[] k = list.ToArray();

int[] x = i.Union(j).ToArray<int>(); //重複を除く

int[] x = i.Concat(j).ToArray<int>(); //重複を残る

int n = Array.BinarySearch(i,3); //検索(含まれる場合に0を返す)

★Dictionaryについて

●初期化

// 方法1

Dictionary<int, String> dect1 = new Dictionary<int, String>();

dect1.Add(0, "Item1");

dect1.Add(1, "Item2");

// 方法2

Dictionary<int, String> dect2 = new Dictionary<int, String>(dect1);

// 方法3(推薦)

Dictionary<int, String> dect3 = new Dictionary<int, String>

{

{ 0, "item1" },

{ 1, "item2" }

};

Dictionary<int, Student> students = new Dictionary<int, Student>()

{

{ 001, new Student { FirstName = "a", LastName = "A", ID = 111 } },

{ 002, new Student { FirstName = "b", LastName = "B", ID = 222 } },

{ 003, new Student { FirstName = "c", LastName = "C", ID = 333 } }

};

●操作

if(dic.ContainsKey("key"))

{

string value = dic["key"];

...

dic.Remove("key");

}

★いろんなforeach

1.Enum

foreach (string name in Enum.GetNames(typeof(Sample)))

{...}

foreach (string value in Enum.GetValues(typeof(Sample)))

{...}

2.List<T>, Queue<T>, Stack<T>, LinkedList<T>

foreach (string item in list)

{...}

3.HashTable, SortedList, Dictionary<TKey,TValue>, SortedList<TKey,TValue>

foreach (DictionaryEntry item in hashTable)

//foreach (DictionaryEntry item in sortedList)

{

string key = item.Key;

string value = item.Value;

...

}

foreach (string key in hashTable.Keys)

{...}

foreach (string value in hashTable.Values)

{...}

foreach (KeyValuePair<string, string> kvp in dictionay)

{

string key = kvp.Key;

string value = kvp.Value;

...

}

Dictionary<string, int>.KeyCollection keys = dictionay.Keys;

foreach (string key in keys)

{...}

Dictionary<string, int>.ValueCollection values = dictionay.Values;

foreach (int value in values)

{...}

4.DataSet, DataTable

foreach (DataTable dt in dataSet.Tables)

{

string tableName = dt.TableName.ToString();

...

}

foreach (DataRow dr in dataSet.Tables[0].Rows)

//foreach (DataRow dr in dataSet.Tables[0].Select("ID>6 AND ID<12"))

{

string id = dr["ID"].ToString();

...

}

foreach (DataColumn dc in dataSet.Tables[0].Columns)

{

string colName = dc.ColumnName;

...

}

5.DataGridView

foreach (DataGridViewRow dgr in dataGridView1.Rows)

{

string id = dgr.Cells["ID"].ToString();

...

}

6.WinForm

foreach (Control ctl in this.Controls)

{

if (ctl.GetType().Name.Equals("ListBox") || ctl.Name.Equals("listBox1"))

{

this.Controls.Remove(ctl);

}

}

Java

public class MyClass<T> { }

new MyClass<string>()

Map<K, V>

public <T> void method(T t){ }

String s = obj.<String>method();

制約

なし

T extends <baseclass name>

T extends <interface name>

T extends U

同じパッケージ内に共存できない

MyClass, MyClass<T>, MyClass<T,U>

java.lang.Throwable の子孫クラスをジェネリックにすることはできない

オーバーロードできない

public class MyClass {

public void method() { }

public <T> void method() { }

public <T,U> void method() { }

}

this.<String>get(); OK

<String>get(); NG

ジェネリックなコンストラクタは宣言できる

class MyClass {

<T> MyClass() {

}

}

new <String>MyClass();

class MyClass<T> {

<U> MyClass() {

}

}

<String>MyClass<Integer>();

ジェネリックなクラスの静的メンバ (フィールド・メソッド) NG

static T staticField; // 不可

static class MyNestedClass<U extends T> { // 不可

}

class MyClass<T> {

static {

System.out.println("Initialized."); //一回だけ出力される

}

public static void main(String[] args) {

new MyClass<Object>();

new MyClass<String>();

}

}

class MyGenericClass<T extends MyClass> extends Object { }

class MyGenericClass

<T extends MyClass & MyInterface1 & MyInterface2> { }

// インタフェースの制約でも implements ではなく extends と書く

class MyGenericClass

<T extends MyClass, U extends T> { }

メソッド

<T extends MyClass> T method() { }

Tは参照型のみ

int、char や double などの原始型はそのままでは扱えないので

Integer、Char や Double などのラッパークラスを使う必要がある

ジェネリックな型の配列

List[] a1 = new List[3]; // OK (非推奨)

List[] a2 = new List<?>[3]; // OK (非推奨)

List[] a3 = new List<String>[3]; // エラー

List<?>[] a4 = new List[3]; // OK (非推奨)

List<?>[] a5 = new List<?>[3]; // OK

List<?>[] a6 = new List<String>[3]; // エラー

List<String>[] a7 = new List[3]; // 警告 (非推奨)

List<String>[] a8 = new List<?>[3]; // エラー

List<String>[] a9 = new List<String>[3]; // エラー

List<String>[] a10 = (List<String>[]) new List<?>[3]; // 警告

@SuppressWarnings("unchecked")

List<String>[] a11 = (List<String>[]) new List<?>[3]; // OK

ワイルドカード

List<?> list = new ArrayList<String>();

List<?> list = new ArrayList<Object>();

List<?> list = new LinkedList<Number>();

List<?> list = new ArrayList<ArrayList<Object>>();

ワイルドカードの境界には、 extends と super が指定できる

List<? extends Number> list1 = new ArrayList<Integer>();

List<? super Number> list2 = new ArrayList<Object>();

class MyClass1<T> {

List<T> list;

void copy(List<? super T> list2) {

for (T t : list)

list2.add(t);

}

}

class MyClass2<T> {

List<T> list;

void copy(Iterable<? extends T> list2) {

for (T t : list2)

list.add(t);

}

}

サンプル

public static void method(List<? extends Number> list) {

for (Number number : list) {

System.out.println(number);

}

}

List<Integer> list = new ArrayList<Integer>(){

{

add(1);

add(2);

add(3);

}

};

Util.method(list);

List<Object> も List<String> も List<Integer> も結局は単なる List として扱われる

リフレクションにおいて実際のオブジェクトの型を扱えない

instanceof 演算子で型を判別できない

実行時にジェネリックの機能を使用できないため、パフォーマンスの面で不利

★Genericの注意点

JavaのGeneric情報はコンパイル後削除されるため、Generic化されてもBoxingを避けない

public void method(List<String> list)

public void method(List<Integer> list)

※OverLoadできず、コンパイルエラー

List<String>, List<Integer>, List<T> ⇒ List

List<String>[] ⇒ List[]

List<? extends E>, List<? super E> ⇒ List<E>

List<T extends Serializable & Cloneable> ⇒ List<Serializable & Cloneable>

List<String> list1 = new ArrayList<String>();

List<Integer> list2 = new ArrayList<Integer>();

boolean b = list1.getClass() == list2.getClass(); true

boolean b = list1 instanceof List<String>); × コンパイルエラー

boolean b = list1 instanceof List); true

boolean b = list1 instanceof ArrayList); true

List<String> list = new ArrayList<String>(); 〇

List<String>[] listArray1 = new List[6]; 〇 警告

List<String>[] listArray2 = new List<String>[6]; × コンパイルエラー

List<String>[] listArray3 = new ArrayList[6]; 〇 警告

List<String>[] listArray4 = new ArrayList<String>[6]; × コンパイルエラー

改良前

class Example<T>{

※具体的な型が確定できないため

private T t = new T(); × コンパイルエラー

private T[] tArray = new T[6]; × コンパイルエラー

private List<T> list = new ArrayList<T>(); 〇

※private List<Object> list = new ArrayList<Object>()と同じ

}

改良後

class Example<T>{

private T t;

private T[] tArray;

private List<T> list = new ArrayList<T>();

public Example() {

try{

Class<?> type = Class.forName("xxx");

t = (T)type.newInstance();

tArray = (T[])Array.newInstance(type, 6);

}catch(Exception e){

e.printStackTrace();

}

}

}

補足

public static <E> void append(List<E> list, Class<E> cls)

throws InstantiationException, IllegalAccessException {

//E elem = new E(); // NG

E elem = cls.newInstance(); // OK

list.add(elem);

}

★Wildcardの注意点

・read処理のみの場合、上限を設定、親クラスに対する操作

public static <E> void read(List<? extends E> list){

for(E e : list){

// do something

}

}

・write処理のみの場合、下限を設定、子クラスに対する操作

public static void write(List<? super Number> list){

list.add(12);

list.add(3.6);

}

・read処理とwrite処理の場合

List<E>

サンプル(Collections.copy)

public static <T> void copy(List<? super T> dest, List<? extends T> src){

...

for(int i = 0; i < src.size(); i++){

dest.set(i, src.get(i)); // srcはreadのみ、destはwriteのみ

}

...

}

PECS(producer-extends, consumer-super)

public void pushAll(Iterable<? extends E> src) {

for (E e : src) {

push(e);

}

}

public void popAll(Collection<? super E> dst) {

while (!isEmpty()) {

dst.add(pop());

}

}

<? extends T> Retrieve objects from a collection.

<? super T> Put objects in a collection.

class Apple extends Fruit { ... }

List<Apple> apples = new ArrayList<>();

apples.add(new Apple());

List<? extends Fruit> basket = apples;

basket.add(new Apple()); //Compile error

basket.add(new Fruit()); //Compile error

List<? super Apple> basket = apples;

basket.add(new Apple()); //OK

basket.add(new Fruit()); //Compile error

★Generic中のcovarianceとcontravariance

covariance 子クラスで親クラスを替える

contravariance 親クラスで子クラスを替える

class Base{

public Number method(){

// do something

}

public void method(Integer i){

// do something

}

}

class Sub extends Base{

@Override

public Integer method(){ ←covarianceの例 Overrideできる

// do something

}

public void method(Number n){ ←contravarianceの例 Overloadと相当

// do something

}

}

・Genericはcovarianceなし

Number[] n = new Integer[6]; 〇

List<Number> list = new ArrayList<Integer>(); ×

List<? extends Number> list = new ArrayList<Integer>(); 〇 真似できる

・Genericはcontravarianceなし

Integer[] n = new Number[6]; ×

List<Integer> list = new ArrayList<Number>(); ×

List<? super Integer> list = new ArrayList<Number>(); 〇 真似できる

★List<T>とList<?>

List<T> add, removeなど使える

List<?> add使えないが、remove, clear使える

★サンプル

例1

static <E> E reduce(List<E> list, Function<E> func, E initVal) {

List<E> innerList = new ArrayList<E>(list);

E result = initVal;

for (E e : innerList) {

result = func.apply(result, e);

}

return result;

}

interface Function<E> {

E apply(E arg1, E arg2);

}

例2

//public static <T extends Comparable<? super T>> T max(List<? extends T> l) {

public static <T extends Comparable<T>> T max(List<T> list) {

Iterator<T> i = list.iterator();

T result = i.next();

while (i.hasNext()) {

T t = i.next();

if (t.compareTo(result) > 0) {

result = t;

}

}

return result;

}

例3

public static <E> Set<E> union(Set<E> s1, Set<E> s2) {

Set<E> result = new HashSet<E>(s1);

result.addAll(s2);

return result;

}

public static <E> Set<E> union(Set<? extends E> s1, Set<? extends E> s2) {

...

}

Set<Integer> integers = new HashSet<Integer>();

Set<Double> doubles = new HashSet<Double>();

//Set<Number> numbers = union(integers, doubles); ×

Set<Number> numbers = Xxx.<Number>union(integers, doubles);

★Map Distinct Value Types (typesafe heterogeneous container)

public class Context {

private final Map<Class<?>, Object> values = new HashMap<>();

public <T> void put(Class<T> key, T value) {

if (key == null) {

throw new NullPointerException("key is null");

}

values.put(key, key.cast(value));

}

public <T> T get(Class<T> key) {

return key.cast(values.get(key));

}

}

context.put(Runnable.class, runnable);

context.put(Executor.class, executor);

Runnable value = context.get(Runnable.class);

Runnable[] value = context.get(Runnable[].class);

List<Runnable> value = context.get(List.class); // NG