Anonymous
Anonymous

Reputation: 3404

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?

Upvotes: 12

Views: 5384

Answers (6)

michid
michid

Reputation: 10814

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.

Upvotes: 0

schmmd
schmmd

Reputation: 19448

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.

Upvotes: 3

Dan Cecile
Dan Cecile

Reputation: 2493

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));
    }
}

Upvotes: 3

emory
emory

Reputation: 10891

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.

Upvotes: 1

Bohemian
Bohemian

Reputation: 424983

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
}

Upvotes: 9

Ryan Stewart
Ryan Stewart

Reputation: 128779

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;

Upvotes: 2

Related Questions