プログラム言語 ジェネリック
概要
設計時でなく、インスタンス作成時に型を決められる配列(クラス)
型だけ違って処理の内容が同じクラスを設計する際に用いる
ジェネリクス、ジェネリックス
(generic/generics)
ラッパークラス
ラップ(包む)
クラスの機能を利用する際に、
継承するのではなく、クラス内でそのクラスをインスタンス化&メソッドを呼び出し、
外部からは利用するクラスが見えない様に包み込む方法
class Other{
void method(){ ~ }
}
class Mine{
Other other;
Mine(){
this.other = new Other();
}
void method(){
this.other.method();
}
}
public static void main( ~ ){
Mine mine = new Mine();
mine.method();
※内部ではOther.method()が呼ばれている。
}
または、JAVAにおいて、基本型をObjectとして用いる為のクラス
ここではジェネリックに渡す引数
void method(Object obj)
~
}
method(引数);
※引数にはObject型を基底クラスとする型しか使えない
int等の基本型は×
Integer i = new Integer(100);
method(i);
は可。
なお、IDEの機能により結局直接数値を渡してもコンパイル可。
byte | → | Byte |
short | → | Short |
int | → | Integer |
long | → | Long |
float | → | Float |
double | → | Double |
char | → | Character |
boolean | → | Boolean |
汎用的な型
//Tは汎用的な型(命名は自由)
public class MyList<T>
{
T[] _list = new T[1000];
public T this[int index]
{
get { return _list[index]; }
set { _list[index] = value; }
}
}
public void Form1_Load(object sender, EventArgs e)
{
MyList<int> myList = new MyList<int>();
※T:intとしてインスタンスを作成
for (var i = 0; i < 10; i++)
{
myList[i] = i;
※myList[i]にstring型の値を入れるとエラー
}
}
コンパイル時にコードを生成する機能
型だけが異なる大量のコードを生成する時等に役立つ。
ベクターについては「プログラム言語 配列・リスト/リスト(コレクション)」参照
#include <vector>
#include <string>
汎用的な型:Tを定義
template <typename T>
class Stack
{
private:
std::vector<T> stack_;
public:
void Push(T value);
T Pop();
bool IsEmpty();
};
template <typename T>
void Stack<T>::Push(T value)
{
this->stack_.push_back(value);
}
template <typename T>
T Stack<T>::Pop()
{
T value = this->stack_.back();
this->stack_.pop_back();
return value;
}
template <typename T>
bool Stack<T>::IsEmpty()
{
if (this->stack_.empty())
{
return true;
}
else
{
return false;
}
}
int main(array<System::String ^> ^args)
{
// インスタンス作成時に型を決められる
Stack<std::string> *myStack = new Stack<std::string>();
bool myBool = myStack->IsEmpty();
// myBool:True
myStack->Push("A");
myStack->Push("B");
myStack->Push("C");
std::string myStr = myStack->Pop();
// myStr:"C"
myBool = myStack->IsEmpty();
// myBool:False
return 0;
}
Public Class MyList(Of T)
Dim _list(1000) As T
Default Public Property list(ByVal index As Integer) As T
Get
Return _list(index)
End Get
Set(value As T)
_list(index) = value
End Set
End Property
End Class
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
Dim myList As New MyList(Of Integer)
※T:Integerとしてインスタンスを作成
For i As Integer = 0 To 10 Step 1
myList(i) = i
※myList[i]にstring型の値を入れるとエラー
Next i
End Sub
public class MyGeneric<T> {
T t;
public MyGeneric(T prmT){
this.t = prmT;
}
public T getT(){
return this.t;
}
}
public class StudyMain {
public static void main(String[] args) {
MyGeneric<String> m1 = new MyGeneric<String>("java");
String s = m1.getT();
MyGeneric<Integer> m2 = new MyGeneric<Integer>(100);
Integer i = m2.getT();
}
}
ジェネリックメソッド
public class MyGeneric {
public <T> T retT(T prmT){
T t = prmT;
return t;
}
}
public class StudyMain {
public static void main(String[] args) {
MyGeneric m1 = new MyGeneric();
String s = m1.retT("Java");
MyGeneric m2 = new MyGeneric();
int i = m2.retT(5);
}
}
ジェネリックインターフェース
public interface MyInterface<T> {
public T myMethod();
public void myMethod(T t);
}
public class MyGeneric implements MyInterface<String> {
@Override
public String myMethod() {
return "Java";
}
@Override
public void myMethod(String t) {
~
}
}
public class StudyMain {
public static void main(String[] args) {
MyGeneric m = new MyGeneric();
String s = m.myMethod();
m.myMethod("Java");
}
}
静的ジェネリッククラスコンストラクタ
{
public static T myValue { get; set; }
static MyClass()
{
Console.WriteLine(@"初期化");
}
}
MyClass<string>.myValue = @"aaa";
MyClass<int>.myValue = 100;
MyClass<string>.myValue = @"bbb";
Console.WriteLine(MyClass<string>.myValue);
Console.WriteLine(MyClass<int>.myValue);
→
初期化 ※T=string時
初期化 ※T=int時
bbb
100
※最初の使用時のみコンストラクタが実行される。
制約
基本クラス制約
型(T)=基本クラス(基底クラス)
class Parent
{
public string pFunc(string str) { return str; }
}
class Child : Parent { }
class Other {}
型(T)=基本クラス
class Test<T> where T : Parent
{
T obj;
public Test(T o) { this.obj = o; }
T はParent型。o はParentのインスタンス
public void testFunc(string prm)
{
string revStr = this.obj.pFunc(str: prm);
this.objはParentのインスタンスなので、Parentのメンバー関数を呼び出し可能
Console.WriteLine(revStr);
}
}
class Program
{
static void Main(string[] args)
{
Parent p = new Parent();
Child c = new Child();
Test<Parent> t1 = new Test<Parent>(o: p);
t1.testFunc(prm: @"aaa");
基本クラス制約の型(クラス)は派生クラスでもOK
Test<Child> t2 = new Test<Child>(o: c);
t2.testFunc(prm: @"aaa");
Other o = new Other();
Test<Other> t3 = new Test<Other>(o: o);
→コンパイルエラー:Parentを継承していないクラスを型パラメータとして指定できない
Test<string> t3 = new Test<string>(o: @"aaa");
→コンパイルエラー:Parentを継承していないクラスを型パラメータとして指定できない
}
}
public T myMethod() {
T t = null;
return t;
}
public void myMethod(T t) {
~
}
}
public class StudyMain {
public static void main(String[] args) {
MyGeneric<Integer> m1 = new MyGeneric<Integer>();
int i = m1.myMethod();
m1.myMethod(100);
m1.myMethod(5.5); →エラー
MyGeneric<Double> m2 = new MyGeneric<Double>();
Double d = m2.myMethod();
m2.myMethod(5.5);
m2.myMethod(100); →エラー
MyGeneric<String> m1 = new MyGeneric<String>(); →エラー
}
}
ワイルドカード
public class X{
public String toString(){
return "X";
}
}
public class Y extends X{
public String toString(){
return "Y";
}
}
public class Main {
list = X、またはXの子クラス
public static void method1(List<? extends X> list){
実行時まで型不明の為、型を指定したaddはコンパイルエラー
list.add(new X()); エラー
list.add(new Y()); エラー
System.out.println(list.get(0));
}
list = Y、またはYの親クラス
public static void method2(List<? super Y> list){
実行時まで型不明の為、型を指定したaddはコンパイルエラー
list.add(new X()); エラー
list.add(new Y()); YはOK
System.out.println(list.get(0));
System.out.println(list.get(1));
}
public static void main(String[] args) {
List<X> l1 = new ArrayList<>(); l1.add(new X());
List<Y> l2 = new ArrayList<>(); l2.add(new Y());
method1(l1); →X
method1(l2); →Y
method2(l1); →X Y
method2(l2); →Y Y
}
}
インターフェイス制約
型(T)=指定のインターフェイスを実装している型(クラス)
interface IParent
{
string pFunc(string str);
}
class Child : IParent{ }
class Other {}
型(T)=インターフェイス
class Test<T> where T : IParent
{
T obj;
public Test(T o) { this.obj = o; }
T はIParentを継承した型(クラス)。o はそのインスタンス
public void testFunc(string prm)
{
string revStr = this.obj.pFunc(str: prm);
this.objはIParentを継承したクラスのインスタンスなので、IParentのメンバー関数を呼び出し可能
Console.WriteLine(revStr);
}
}
class Program
{
static void Main(string[] args)
{
Child c = new Child();
Test<Child> t1 = new Test<Child>(o: c);
t1.testFunc(prm: @"aaa");
Other o = new Other();
Test<Other> t2 = new Test<Other>(o: o);
→コンパイルエラー:IParentを継承していないクラスを型パラメータとして指定できない
Test<string> t3 = new Test<string>(o: @"aaa");
→コンパイルエラー:IParentを継承していないクラスを型パラメータとして指定できない
}
}
コンストラクタ制約
型(T)=デフォルトコンストラクタが実装されている型(クラス)
{
public MyClass1(string prm){}
}
class MyClass2
{
public MyClass2(){}
public MyClass2(string prm){}
}
class Test<T> where T : new()
{
public Test()
{
T t = new T();
コンストラクタ制約があるのでジェネリック型(クラス)のインスタンスを作成できる。
制約が無ければコンパイルエラー
}
}
static void Main(string[] args)
{
Test<MyClass1> t1 = new Test<MyClass1>();
MyClass1はデフォルトコンストラクタが定義されていないのでコンパイルエラー
Test<MyClass2> t2 = new Test<MyClass2>();
MyClass2はデフォルトコンストラクタがあるので多重定義があってもOK
}
newInstanceメソッドで代替する
public <T> T func(Class<T> clazz, String prm) {
T t = null;
try {
t = clazz.newInstance();
t.~ = prm
} catch (InstantiationException e) {
~
}
return t;
}
参照型制約
型(T)=参照型
{
T obj;
public Test(T prm)
{
this.obj = prm;
}
}
static void Main(string[] args)
{
Test<string> t = new Test<string>(prm: @"aaa");
Test<string> t = new Test<string>(prm: null);
Test<string> t = new Test<string>(prm: string.Empty);
Test<MyClass> t = new Test<MyClass>(prm: new MyClass());
Test<Test<string>> t = new Test<Test<string>>(prm: new Test<string>(prm: @"aaa"));
↓ コンパイルエラー
Test<int> t = new Test<int>(prm: 5);
Test<bool> t = new Test<bool>(prm: true);
}
値照型制約
型(T)=値型
{
T obj;
public Test(T prm)
{
this.obj = prm;
}
}
struct MyStruct
{
public int myInt;
}
class MyClass { }
static void Main(string[] args)
{
Test<int> t = new Test<int>(prm: 5);
Test<bool> t = new Test<bool>(prm: true);
Test<MyStruct> t = new Test<MyStruct>(prm: new MyStruct() { myInt = 5 });
↓ コンパイルエラー
Test<string> t = new Test<string>(prm: string.Empty);
Test<MyClass> t = new Test<MyClass>(prm: new MyClass());
}
制約の順番
(1)参照型制約、値型制約、基本クラス制約
どれか1つだけ
(2)インターフェイス制約
(3)コンストラクタ制約
interface IMyInterface{}
class MyClass{}
○:class Test<T> where T : MyClass, new() { }
○:class Test<T> where T : class, IMyInterface, new() { }
×:class Test<T> where T : IMyInterface, MyClass, new() { } ※順序違反
×:class Test<T> where T : new(), MyClass, IMyInterface { } ※順序違反
×:class Test<T> where T : class, struct { } ※(1)重複違反
複数の型パラメーターに別の制約を付ける場合
class Test<T1, T2>
where T1 : class
where T2 : struct
{ }