Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Function "composition" and type safety in java

I'm trying to implement a class that isolates a part of a object, and replaces the part with with something else. The part of the object may not have the same type as the object itself.

A simple example would be a class that takes the string "--12--", isolates the ascii number, and replaces it with the next natural number. So, the whole sequence would be "--12--" -> "12" -> 12 -> 13 -> "13" -> "--13--".

With this in mind, I implemented the following:

public abstract class Replacer<Outer, Inner>
    {
    protected abstract Inner decompose(Outer something);
    protected abstract Outer compose(Inner something);
    protected abstract Inner inner_replace(Inner something);
    public Outer replace(Outer something)
        {
        Inner s = decompose(something);
        s = inner_replace(s);
        return compose(s);
        }
    }

Now, I want to be able to compose a series of Replacers - stack them, so that each one computes its inner_replace by using the "lower" Replacer:

public abstract class ComposableReplacer<Outer, Inner> extends Replacer<Outer, Inner>
    {
    protected Replacer<Inner, ?> child;

    @Override
    public Outer replace(Outer something)
        {
        Inner s = decompose(something);
        s = inner_replace(s);
        if (child!=null)
            s= child.replace(s);
        return compose(s);
        }

    }

So, far, this works correctly, but now I'm trying to write a convenience method to take a couple of ComposableReplacers and stack them automatically:

public static <I, O> ComposableReplacer<I, O> compose(ComposableReplacer<?, ?>... rs)
    {
    for (int i=0; i<rs.length-1; i++)
        rs[i].child= rs[i+1];
    return rs[0];
    }

This fails, since each ComposableReplacer's inner type must be the outer type of its child and the compiler can't infer that from a array of ComposableReplacer<?, ?>.

Is it possible to do this in java (and still have type safety)?

EDIT To be clear, the problem is declaring a method that takes an array of ComposableReplacer and stacks/chains them, with type safety.

like image 359
loopbackbee Avatar asked Nov 08 '13 12:11

loopbackbee


1 Answers

Even if there was support for generic arrays your code would fail due to a logical error. An array consists of elements of the same type but what you want to do does not work with items of the same type. This becomes clear if you try to implement your method with just two parameters instead of varargs:

// won’t work
public static <I, O> ComposableReplacer<I, O> compose(
  ComposableReplacer<I, O> rs1, ComposableReplacer<I, O> rs2) {
  rs1.child=rs2;
  return rs1;
}

This code still doesn’t compile as rs1.child requires a ComposableReplacer<O,?> instead of a ComposableReplacer<I,O> if you fix this, your method becomes

public static <I, O> ComposableReplacer<I, O> compose(
  ComposableReplacer<I, O> rs1, ComposableReplacer<O,?> rs2) {
  rs1.child=rs2;
  return rs1;
}

Now it works, but the two parameters have a different type. If Java had type-safe arrays they had to prevent containing a ComposableReplacer<I, O> and a ComposableReplacer<O,?> at the same time. (Unless you enforce O and I to be the same.)

To illustrate it further, here is the method for three parameter:

public static <I, O, X> ComposableReplacer<I, O> compose(
  ComposableReplacer<I, O> rs1, ComposableReplacer<O,X> rs2,
  ComposableReplacer<X, ?> rs3) {
  rs1.child=rs2;
  rs2.child=rs3;
  return rs1;
}

Here you see that every argument has a different type and you need an additional type parameter, so using a “type safe array” (read a java.util.List) can’t provide that. The simplest solution is to keep a two argument method and let the caller invoke it multiple times. Or n-arg if you know that the use case of n args will be required very often.

like image 197
Holger Avatar answered Nov 13 '22 02:11

Holger