1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

VB.NET 4.8.1を今の時代に触る!!PHP 8.x, TypeScript 5.xとの言語仕様比較(第9章 ジェネリクス)

1
Last updated at Posted at 2025-12-27

目次
第8章 クラスとオブジェクト指向

第9章 ジェネリクス

9.1 ジェネリック型の定義

PHP(PHPDoc + 静的解析ツール)

<?php
// PHP にはネイティブのジェネリクスがない
// PHPDoc で型ヒントを提供し、PHPStan/Psalm で検証

/**
 * @template T
 */
class Box {
    /** @var T */
    private mixed $value;

    /** @param T $value */
    public function __construct(mixed $value)
    {
        $this->value = $value;
    }

    /** @return T */
    public function getValue(): mixed
    {
        return $this->value;
    }
}

/** @var Box<string> */
$stringBox = new Box("Hello");

/** @var Box<int> */
$intBox = new Box(42);

TypeScript

// ジェネリッククラス
class Box<T> {
    constructor(private value: T) {}

    getValue(): T {
        return this.value;
    }

    map<U>(fn: (value: T) => U): Box<U> {
        return new Box(fn(this.value));
    }
}

const stringBox = new Box<string>("Hello");
const intBox = new Box(42);  // Box<number> と推論

// ジェネリック関数
function identity<T>(value: T): T {
    return value;
}

const str = identity("hello");  // string と推論
const num = identity<number>(42);  // 明示的に指定

// ジェネリックインターフェース
interface Container<T> {
    value: T;
    getValue(): T;
}

// ジェネリック型エイリアス
type Result<T, E = Error> =
    | { success: true; value: T }
    | { success: false; error: E };

VB.NET

' ジェネリッククラス
Class Box(Of T)
    Private _value As T

    Public Sub New(value As T)
        _value = value
    End Sub

    Public Function GetValue() As T
        Return _value
    End Function

    Public Function Map(Of U)(fn As Func(Of T, U)) As Box(Of U)
        Return New Box(Of U)(fn(_value))
    End Function
End Class

Dim stringBox As New Box(Of String)("Hello")
Dim intBox As New Box(Of Integer)(42)

' ジェネリックメソッド
Function Identity(Of T)(value As T) As T
    Return value
End Function

Dim str = Identity("hello")  ' String と推論
Dim num = Identity(Of Integer)(42)  ' 明示的に指定

' ジェネリックインターフェース
Interface IContainer(Of T)
    ReadOnly Property Value As T
    Function GetValue() As T
End Interface

' 複数の型パラメータ
Class Pair(Of TFirst, TSecond)
    Public Property First As TFirst
    Public Property Second As TSecond

    Public Sub New(first As TFirst, second As TSecond)
        Me.First = first
        Me.Second = second
    End Sub
End Class

Dim pair As New Pair(Of String, Integer)("Age", 30)

9.2 型制約

PHP(PHPDoc)

<?php
/**
 * @template T of Animal
 */
class Shelter {
    /** @var T[] */
    private array $animals = [];

    /** @param T $animal */
    public function add(mixed $animal): void
    {
        $this->animals[] = $animal;
    }
}

// クラス制約
interface Comparable {
    public function compareTo(self $other): int;
}

/**
 * @template T of Comparable
 * @param T[] $items
 * @return T|null
 */
function findMax(array $items): mixed
{
    // ...
}

TypeScript

// extends による制約
function getLength<T extends { length: number }>(obj: T): number {
    return obj.length;
}

getLength("hello");     // OK
getLength([1, 2, 3]);   // OK
// getLength(42);       // エラー

// インターフェース制約
interface Comparable<T> {
    compareTo(other: T): number;
}

function findMax<T extends Comparable<T>>(items: T[]): T | undefined {
    return items.reduce((max, item) =>
        item.compareTo(max) > 0 ? item : max
    );
}

// keyof 制約
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
    return obj[key];
}

const person = { name: "John", age: 30 };
const name = getProperty(person, "name");  // string
// getProperty(person, "invalid");  // エラー

// 複数の制約
function merge<T extends object, U extends object>(a: T, b: U): T & U {
    return { ...a, ...b };
}

VB.NET

' クラス制約
Class Shelter(Of T As Animal)
    Private animals As New List(Of T)

    Public Sub Add(animal As T)
        animals.Add(animal)
    End Sub
End Class

' インターフェース制約
Interface IComparable(Of T)
    Function CompareTo(other As T) As Integer
End Interface

Function FindMax(Of T As IComparable(Of T))(items As IEnumerable(Of T)) As T
    Return items.Aggregate(Function(max, item)
        If item.CompareTo(max) > 0 Then Return item Else Return max
    End Function)
End Function

' 複数の制約
Class Repository(Of T As {Class, IEntity, New})
    ' T は参照型で、IEntityを実装し、パラメータなしコンストラクタを持つ
    Public Function Create() As T
        Return New T()
    End Function
End Class

' 利用可能な制約
' Class       - 参照型
' Structure   - 値型
' New         - パラメータなしコンストラクタ
' インターフェース名 - 特定のインターフェースを実装
' クラス名    - 特定のクラスを継承

' 複数制約の例
Class Service(Of T As {Class, IDisposable, New})
    Public Function CreateAndUse() As String
        Using instance As T = New T()
            Return instance.ToString()
        End Using
    End Function
End Class

9.3 共変性と反変性

概念

用語 意味
共変(Covariant) 派生型に置換可能 IEnumerable<Dog>IEnumerable<Animal>
反変(Contravariant) 基底型に置換可能 Action<Animal>Action<Dog>
不変(Invariant) 置換不可 List<Dog>List<Animal>

PHP(型システムの制限)

<?php
// PHP の配列は共変的に振る舞う(実行時)
class Animal {}
class Dog extends Animal {}

/** @param Animal[] $animals */
function feedAll(array $animals): void {
    foreach ($animals as $animal) {
        // feed
    }
}

/** @var Dog[] */
$dogs = [new Dog(), new Dog()];
feedAll($dogs);  // OK(配列は共変)

TypeScript

// TypeScript は構造的型付けなので共変・反変は暗黙的

// 関数の引数は反変、戻り値は共変
type AnimalHandler = (animal: Animal) => void;
type DogHandler = (dog: Dog) => void;

// これは安全ではないが、TypeScript では許可される(--strictFunctionTypes で制限)
const animalHandler: AnimalHandler = (a: Animal) => console.log(a);
const dogHandler: DogHandler = animalHandler;  // 反変

// 戻り値は共変
type AnimalFactory = () => Animal;
type DogFactory = () => Dog;

const dogFactory: DogFactory = () => new Dog();
const animalFactory: AnimalFactory = dogFactory;  // 共変

// ReadonlyArray は共変
const dogs: readonly Dog[] = [new Dog()];
const animals: readonly Animal[] = dogs;  // OK

// 通常の配列は不変
// const mutableAnimals: Animal[] = dogs;  // エラー

VB.NET

' Out = 共変(出力位置のみ)
Interface IProducer(Of Out T)
    Function Produce() As T
End Interface

' In = 反変(入力位置のみ)
Interface IConsumer(Of In T)
    Sub Consume(item As T)
End Interface

Class Animal
End Class

Class Dog
    Inherits Animal
End Class

' 共変の例
Class DogProducer
    Implements IProducer(Of Dog)

    Public Function Produce() As Dog Implements IProducer(Of Dog).Produce
        Return New Dog()
    End Function
End Class

Dim dogProducer As IProducer(Of Dog) = New DogProducer()
Dim animalProducer As IProducer(Of Animal) = dogProducer  ' OK(共変)

' 反変の例
Class AnimalConsumer
    Implements IConsumer(Of Animal)

    Public Sub Consume(item As Animal) Implements IConsumer(Of Animal).Consume
        Console.WriteLine("Consuming animal")
    End Sub
End Class

Dim animalConsumer As IConsumer(Of Animal) = New AnimalConsumer()
Dim dogConsumer As IConsumer(Of Dog) = animalConsumer  ' OK(反変)

' .NET の標準型における共変・反変
' IEnumerable(Of Out T)  - 共変
' IComparer(Of In T)     - 反変
' Func(Of Out TResult)   - 共変
' Action(Of In T)        - 反変

Dim dogs As IEnumerable(Of Dog) = New List(Of Dog)()
Dim animals As IEnumerable(Of Animal) = dogs  ' OK

Dim animalComparer As IComparer(Of Animal) = Nothing
Dim dogComparer As IComparer(Of Dog) = animalComparer  ' OK

9.4 ジェネリックの実用例

コレクション操作

// TypeScript
function filter<T>(arr: T[], predicate: (item: T) => boolean): T[] {
    return arr.filter(predicate);
}

function map<T, U>(arr: T[], transform: (item: T) => U): U[] {
    return arr.map(transform);
}

function reduce<T, U>(
    arr: T[],
    reducer: (acc: U, item: T) => U,
    initial: U
): U {
    return arr.reduce(reducer, initial);
}
' VB.NET
Function Filter(Of T)(arr As IEnumerable(Of T),
                      predicate As Func(Of T, Boolean)) As IEnumerable(Of T)
    Return arr.Where(predicate)
End Function

Function Map(Of T, U)(arr As IEnumerable(Of T),
                      transform As Func(Of T, U)) As IEnumerable(Of U)
    Return arr.Select(transform)
End Function

Function Reduce(Of T, U)(arr As IEnumerable(Of T),
                         reducer As Func(Of U, T, U),
                         initial As U) As U
    Return arr.Aggregate(initial, reducer)
End Function

リポジトリパターン

// TypeScript
interface Entity {
    id: string;
}

interface Repository<T extends Entity> {
    findById(id: string): Promise<T | null>;
    findAll(): Promise<T[]>;
    save(entity: T): Promise<T>;
    delete(id: string): Promise<void>;
}

class InMemoryRepository<T extends Entity> implements Repository<T> {
    private items = new Map<string, T>();

    async findById(id: string): Promise<T | null> {
        return this.items.get(id) ?? null;
    }

    async findAll(): Promise<T[]> {
        return Array.from(this.items.values());
    }

    async save(entity: T): Promise<T> {
        this.items.set(entity.id, entity);
        return entity;
    }

    async delete(id: string): Promise<void> {
        this.items.delete(id);
    }
}
' VB.NET
Interface IEntity
    Property Id As String
End Interface

Interface IRepository(Of T As IEntity)
    Function FindByIdAsync(id As String) As Task(Of T)
    Function FindAllAsync() As Task(Of IEnumerable(Of T))
    Function SaveAsync(entity As T) As Task(Of T)
    Function DeleteAsync(id As String) As Task
End Interface

Class InMemoryRepository(Of T As {Class, IEntity})
    Implements IRepository(Of T)

    Private ReadOnly items As New Dictionary(Of String, T)

    Public Async Function FindByIdAsync(id As String) As Task(Of T) _
        Implements IRepository(Of T).FindByIdAsync
        Dim result As T = Nothing
        items.TryGetValue(id, result)
        Return result
    End Function

    Public Async Function FindAllAsync() As Task(Of IEnumerable(Of T)) _
        Implements IRepository(Of T).FindAllAsync
        Return items.Values.ToList()
    End Function

    Public Async Function SaveAsync(entity As T) As Task(Of T) _
        Implements IRepository(Of T).SaveAsync
        items(entity.Id) = entity
        Return entity
    End Function

    Public Async Function DeleteAsync(id As String) As Task _
        Implements IRepository(Of T).DeleteAsync
        items.Remove(id)
    End Function
End Class

第10章 コレクションと配列

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?