Чистий еквівалент для клавіатури Java класу <>?

Я - .NET-парень, тому дозвольте мені спочатку заявити про моє розуміння декількох концепцій Java - виправити мене, якщо я помиляюся.

Java Generics підтримує концепцію обмежених підстановок :

class GenericClass< ? extends IInterface> { ... }

... який схожий на .NET де обмеження:

class GenericClass where T: IInterface { ... }

Java Class описує тип і грубо еквівалентно .NET клас Type

So far, so good. But I can't find a close enough equivalence to the Java genericly typed Class where T is a bounded wildcard. This basically imposes a restriction on the types that the Class represents.

Дозвольте мені навести приклад у Java.

String custSortclassName = GetClassName(); //only known at runtime, 
                                          //e.g. it can come from a config file
Class<? extends IExternalSort> customClass 
    = Class.forName("MyExternalSort")
        .asSubclass(IExternalSort.class);  //this checks for correctness

IExternalSort impl = customClass.newInstance(); //look ma', no casting!

Найближчий, який я міг би отримати в .NET, це щось на зразок цього:

String custSortclassName = GetClassName(); //only known at runtime, 
                                          //e.g. it can come from a config file

Assembly assy = GetAssembly();             //unimportant 

Type customClass = assy.GetType(custSortclassName);
if(!customClass.IsSubclassOf(typeof(IExternalSort))){
    throw new InvalidOperationException(...);
}
IExternalSort impl = (IExternalSort)Activator.CreateInstance(customClass);

Версія Java виглядає більш чистою для мене. Чи є спосіб покращити колегу. NET?

15
@Ascratt Я не бачу, як це стосується мого питання. Цей трюк може бути корисним, якщо вся інформація типу є відома під час компіляції, однак тип MyExternalSort не відомий до часу виконання - цілком може бути реалізований клієнтом моєї бібліотеки сортування, і його визначають лише за назвою.
додано Автор Cristi Diaconescu, джерело
@jalf Я знаю про видалення типу, тому я, звичайно, не очікую відображення 1: 1. Я просто сподіваюся на деяке поліпшення у порівнянні з моєю не дуже гарна реалізація.
додано Автор Cristi Diaconescu, джерело
@jalf Я знаю про видалення типу, тому я, звичайно, не очікую відображення 1: 1. Я просто сподіваюся на деяке поліпшення у порівнянні з моєю не дуже гарна реалізація.
додано Автор Cristi Diaconescu, джерело
Я багато не знаю про генотипи Java, але я знаю, що вони реалізовані по-різному (немає підтримки JVM), і підтримують цілий ряд речей, які не використовуються. Тому не очікуйте знайти прямого "еквівалента" для всього
додано Автор jalf, джерело
Я багато не знаю про генотипи Java, але я знаю, що вони реалізовані по-різному (немає підтримки JVM), і підтримують цілий ряд речей, які не використовуються. Тому не очікуйте знайти прямого "еквівалента" для всього
додано Автор jalf, джерело
Це може бути трохи занадто простим, але не можна було б просто додати обмеження new() до того, де обмеження? Щось на зразок MyExternalSort : IExternalSort, де T: IExternalSort, new() , а потім використовуйте var impl = new T (); ? Особисто я буду використовувати фабричний шаблон, щоб створити екземпляри типів, що реалізують один і той же інтерфейс, у таких сценаріях.
додано Автор Carsten, джерело
@ Крісті Діаконеску: спеціалізований тип не повинен бути відомий під час компіляції. Він просто повинен відповідати вашому обмеженню (успадковане від IExternalSort, конструктора без параметрів) на час компіляції, тому ви також можете надати деякий параметр як екземпляр IExternalSort, коли ви не знаєте його точний тип.
додано Автор Carsten, джерело
@ Крісті Діаконеску: спеціалізований тип не повинен бути відомий під час компіляції. Він просто повинен відповідати вашому обмеженню (успадковане від IExternalSort, конструктора без параметрів) на час компіляції, тому ви також можете надати деякий параметр як екземпляр IExternalSort, коли ви не знаєте його точний тип.
додано Автор Carsten, джерело

7 Відповіді

Using extension methods & a custom wrapper class for System.Type, you can get pretty close to the Java syntax.

NOTE: Type.IsSubclassOf cannot be used to test if a type implements an interface - see the linked documentation on MSDN. One can use Type.IsAssignableFrom instead - see the code below.

using System;

class Type
{
    readonly Type type;

    public Type(Type type)
    {
       //Check for the subtyping relation
        if (!typeof(T).IsAssignableFrom(type))
            throw new ArgumentException("The passed type must be a subtype of " + typeof(T).Name, "type");

        this.type = type;
    }

    public Type UnderlyingType
    {
        get { return this.type; }
    }
}

static class TypeExtensions
{
    public static Type AsSubclass(this System.Type type)
    {
        return new Type(type);
    }
}

// This class can be expanded if needed
static class TypeWrapperExtensions
{
    public static T CreateInstance(this Type type)
    {
        return (T)Activator.CreateInstance(type.UnderlyingType);
    }
}

Подальші удосконалення за допомогою дисперсії інтерфейсу

(Should only be used in production code after the performance has been evaluated. Could be improved by using a (concurrent!) cache dictionary ConcurrentDictionary)

Using Covariant type parameters, a feature introduced with C# 4.0, and an additional type interface IType, which Type implements, one could make things like the following possible:

// IExternalSortExtended is a fictional interface derived from IExternalSort
IType extendedSort = ...
IType externalSort = extendedSort;//No casting here, too.

Можна навіть зробити:

using System;

interface IType
{
    Type UnderlyingType { get; }
}

static class TypeExtensions
{
    private class Type : IType
    {
        public Type UnderlyingType
        {
            get { return typeof(T); }
        }
    }

    public static IType AsSubclass(this System.Type type)
    {
        return (IType)Activator.CreateInstance(
           typeof(Type<>).MakeGenericType(type)
        );
    }
}

static class TypeWrapperExtensions
{
    public static T CreateInstance(this IType type)
    {
        return (T)Activator.CreateInstance(type.UnderlyingType);
    }
}

Тож можна (явним чином) відтворити між незв'язаними інтерфейсами InterfaceA та InterfaceB , наприклад:

var x = typeof(ConcreteAB).AsSubclass();
var y = (IType)x;

але це не може перемогти цілі вправи.

2
додано

Using extension methods & a custom wrapper class for System.Type, you can get pretty close to the Java syntax.

NOTE: Type.IsSubclassOf cannot be used to test if a type implements an interface - see the linked documentation on MSDN. One can use Type.IsAssignableFrom instead - see the code below.

using System;

class Type
{
    readonly Type type;

    public Type(Type type)
    {
       //Check for the subtyping relation
        if (!typeof(T).IsAssignableFrom(type))
            throw new ArgumentException("The passed type must be a subtype of " + typeof(T).Name, "type");

        this.type = type;
    }

    public Type UnderlyingType
    {
        get { return this.type; }
    }
}

static class TypeExtensions
{
    public static Type AsSubclass(this System.Type type)
    {
        return new Type(type);
    }
}

// This class can be expanded if needed
static class TypeWrapperExtensions
{
    public static T CreateInstance(this Type type)
    {
        return (T)Activator.CreateInstance(type.UnderlyingType);
    }
}

Подальші удосконалення за допомогою дисперсії інтерфейсу

(Should only be used in production code after the performance has been evaluated. Could be improved by using a (concurrent!) cache dictionary ConcurrentDictionary)

Using Covariant type parameters, a feature introduced with C# 4.0, and an additional type interface IType, which Type implements, one could make things like the following possible:

// IExternalSortExtended is a fictional interface derived from IExternalSort
IType extendedSort = ...
IType externalSort = extendedSort;//No casting here, too.

Можна навіть зробити:

using System;

interface IType
{
    Type UnderlyingType { get; }
}

static class TypeExtensions
{
    private class Type : IType
    {
        public Type UnderlyingType
        {
            get { return typeof(T); }
        }
    }

    public static IType AsSubclass(this System.Type type)
    {
        return (IType)Activator.CreateInstance(
           typeof(Type<>).MakeGenericType(type)
        );
    }
}

static class TypeWrapperExtensions
{
    public static T CreateInstance(this IType type)
    {
        return (T)Activator.CreateInstance(type.UnderlyingType);
    }
}

Тож можна (явним чином) відтворити між незв'язаними інтерфейсами InterfaceA та InterfaceB , наприклад:

var x = typeof(ConcreteAB).AsSubclass();
var y = (IType)x;

але це не може перемогти цілі вправи.

2
додано

Генеральні файли C# є дисперсією сайту-декларації, фіксована дисперсія параметра типу.

Java is use-site variance, so once we have a declaration List, we can use it 3 ways

List          //invariant, read/write
List<+Number>         //covariant, read only
List<-NUmber>         //contravariant, write only

Існують плюси і мінуси для обох підходів. Підхід на сайті є, мабуть, більш потужним, хоча він і отримав репутацію, що занадто складний для програмістів. Я думаю, це насправді досить легко зрозуміти

List integers = ...;
List<+Number> numbers = integers; //covariant

На жаль, Java винайшов абсолютно чудовий синтаксис,

List<? extends Number>   // i.e. List<+Number>

як тільки ваш код має декілька з них, він стає дійсно потворним. Ви повинні навчитися пережити це.

Now, in the declaration-site camp, how do we achieve 3 variances on the same class? By having more types - a ReadOnlyList, a WriteOnlyList, and a List extending both. This is not too bad, and one might say it's a better design. But it may become ugly if there are more type parameters. And if the designer of a class did not anticipate it being used variantly, the users of the class have no way to use it variantly.

1
додано
Я думаю, що тепер я розумію, про що відповідає ваша відповідь - хоча це більше пов'язано з цим (новим) запитанням "мого. Можливо, ви можете подивитися.

Генеральні файли C# є дисперсією сайту-декларації, фіксована дисперсія параметра типу.

Java is use-site variance, so once we have a declaration List, we can use it 3 ways

List          //invariant, read/write
List<+Number>         //covariant, read only
List<-NUmber>         //contravariant, write only

Існують плюси і мінуси для обох підходів. Підхід на сайті є, мабуть, більш потужним, хоча він і отримав репутацію, що занадто складний для програмістів. Я думаю, це насправді досить легко зрозуміти

List integers = ...;
List<+Number> numbers = integers; //covariant

На жаль, Java винайшов абсолютно чудовий синтаксис,

List<? extends Number>   // i.e. List<+Number>

як тільки ваш код має декілька з них, він стає дійсно потворним. Ви повинні навчитися пережити це.

Now, in the declaration-site camp, how do we achieve 3 variances on the same class? By having more types - a ReadOnlyList, a WriteOnlyList, and a List extending both. This is not too bad, and one might say it's a better design. But it may become ugly if there are more type parameters. And if the designer of a class did not anticipate it being used variantly, the users of the class have no way to use it variantly.

1
додано
Я думаю, що тепер я розумію, про що відповідає ваша відповідь - хоча це більше пов'язано з цим (новим) запитанням "мого. Можливо, ви можете подивитися.

Ви можете отримати трохи гарніший варіант за допомогою оператора "як":

String custSortclassName = GetClassName();
Assembly assy = GetAssembly();
Type customClass = assy.GetType(custSortclassName);

IExternalSort impl = Activator.CreateInstance(customClass) as IExternalSort;
if(impl==null) throw new InvalidOperationException(...);

Але тут я створюю екземпляр, перш ніж перевіряти його тип, що може бути проблемою для вас.

0
додано
Оператор як також виконує трансляцію. Єдина відмінність полягає в тому, що вона не викидає виняток, якщо гравець не був успішним. Це насправді не полегшить.
додано Автор Carsten, джерело

Ви можете отримати трохи гарніший варіант за допомогою оператора "як":

String custSortclassName = GetClassName();
Assembly assy = GetAssembly();
Type customClass = assy.GetType(custSortclassName);

IExternalSort impl = Activator.CreateInstance(customClass) as IExternalSort;
if(impl==null) throw new InvalidOperationException(...);

Але тут я створюю екземпляр, перш ніж перевіряти його тип, що може бути проблемою для вас.

0
додано
Оператор як також виконує трансляцію. Єдина відмінність полягає в тому, що вона не викидає виняток, якщо гравець не був успішним. Це насправді не полегшить.
додано Автор Carsten, джерело

Ви можете спробувати написати спосіб розширення, як-от:

 static class TypeExtension
    {
        public static I NewInstanceOf(this Type t) 
            where  I: class 
        {
            I instance = Activator.CreateInstance(t) as I;
            if (instance == null)
                throw new InvalidOperationException();
            return instance;
        }
    }

Тоді можна використовувати наступним чином:

String custSortclassName = GetClassName(); //only known at runtime, 
                                          //e.g. it can come from a config file

Assembly assy = GetAssembly();
Type customClass = assy.GetType(custSortclassName);            

IExternalSort impl = customClass.NewInstanceOf();
0
додано
ІТ КПІ - Java
ІТ КПІ - Java
436 учасників