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