开发者

Can I use nested generics (aka higher kinded types) in Java?

I was trying to do something like:


public class MyClass <A, B, C <A, B>开发者_运维百科 > {
  ...
}

But Eclipse highlights "B," and says "unexpected , expected extends". What gives? Are nested generics not allowed?


It's because you haven't defined C as a type that is itself typed with 2 type parameters.
Try something like this:

public class MyClass <A, B, C extends Map<A, B>> {
    // This compiles
}


If your template parameters don't share share a class hierarchy, you can use an interface.

For example:

interface IConverter<TFrom, TTo>
{
    TTo convert(TFrom from);
}

class IntToStringConverter implements IConverter<Integer, String>
{
    public String convert(Integer from)
    {
        return "This is a string: " + from.toString();
    }
}

class ConverterUser<TConverter extends IConverter<TFrom, TTo>, TFrom, TTo>
{
    public ConverterUser()
    {
    }

    private List<TConverter> _converter2;

    private TConverter _converter;

    public void replaceConverter(TConverter converter)
    {
        _converter = converter;
    }

    public TTo convert(TFrom from)
    {
        return _converter.convert(from);
    }
}

class Test
{
    public static void main(String[] args)
    {
        ConverterUser<IntToStringConverter, Integer, String> converterUser =
            new ConverterUser<IntToStringConverter, Integer, String>();

        converterUser.replaceConverter(new IntToStringConverter());

        System.out.println(converterUser.convert(328));
    }
}


This is not possible in Java. See the Type Variables section of the language definition along with Generic Classes and Type Parameters. I recently saw (somewhere) a mention that Java is incapable of this but Scala can do it. This is confirmed by S4.4 of the Scala Language Specification.

This is also somewhat confirmed by the following code compiling successfully.

class MyClass [A, B, C [A, B]] {
}

Compiling in java yielded the follwing answers.

MyClass.java:1: > expected
class MyClass <A, B, C<A, B>> {
                      ^
MyClass.java:1: <identifier> expected
class MyClass <A, B, C<A, B>> {
                        ^
MyClass.java:1: ';' expected
class MyClass <A, B, C<A, B>> {
                           ^
MyClass.java:2: reached end of file while parsing
}
 ^
4 errors

I would guess that there is an easier solution to your problem however, as this is somewhat unusual.


You don't have to declare nested types like that. Simply

class MyClass<A, B, C> {}

And when you create a MyClass, you could do something like

MyClass<List<String>, Set<Date>, Map<Integer, Long>> instance;


I am guessing you want MyClass to be a generic class with type parameters A, B, and C. Furthermore you want C to be a generic class with type parameters A and B.

So that I could write

  1. MyClass < String , Date , Map < String , Date > >
  2. MyClass < String , Date , Hashtable < String , Date > > but not
  3. MyClass < String , Date , ElementVsitor < Date , String > >

Then I don't think you can do that.


This is effectively asking for higher order types in Java. Java does not support this directly, but it can be simulated in a somewhat roundabout way like so

interface H<K, T> { }

Here H encodes a higher order type that takes a type parameter K which itself takes parameter T.

You can use this to e.g. implement a generic functor. Note how fmap is effectively a function from H<K<T>> to H<K<R>> although we cannot directly declare it that way.

public interface Functor<K> {
    <T, R> Function<H<K, T>, H<K, R>> lift(Function<T, R> f);

    default <T, R> H<K, R> fmap(Function<T, R> f, H<K, T> h) {
        return lift(f).apply(h);
    }
}

See also my Github repository for full working examples. Further more have a look at this question, which takes the concept much further still.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜