Abusing Type Erasure: Passing Back the Same Subclass the Object Yields
Tags: java, generics, polymorphism
Published: 2018-01-22 16:24:16

---
title: "Abusing Type Erasure: Passing Back the Same Subclass the Object Yields"
published: true
description: Using type erasure to solve a polymorphism type-safety problem
tags: java,generics,polymorphism
---

Java's type erasure generics are generally limited compared to real generics/templates because you don't have access to the actual class object(unless you pass it as runtime parameter) so you can't create new instances of it, create arrays of it, or inspect it. But recently I've encountered a case where type erasure is actually helpful.

# The problem

Lets say we have this interface:

```java
public interface Foo {
    public Object createValue();
    public void handleValue(Object value);
    public void printValue(Object value);
}
```

Implemented by these classes:
```java
import java.util.LinkedList;
import java.util.List;

public class Bar implements Foo {
    @Override
    public Object createValue() {
        List<String> list = new LinkedList<>();
        list.add("one");
        list.add("two");
        list.add("three");
        return list;
    }

    @Override
    @SuppressWarnings("unchecked")
    public void handleValue(Object value) {
        List<String> list = (List<String>) value;
        for (int i = 0; i < list.size(); ++i) {
            list.set(i, "(" + list.get(i) + ")");
        }
    }

    @Override
    @SuppressWarnings("unchecked")
    public void printValue(Object value) {
        List<String> list = (List<String>) value;
        for (String entry : list) {
            System.out.printf("- %s\n", entry);
        }
    }
}

public class Baz implements Foo {
    @Override
    public Object createValue() {
        return new int[]{ 1, 2, 3 };
    }

    @Override
    public void handleValue(Object value) {
        int[] array = (int[]) value;
        for (int i = 0; i < array.length; ++i) {
            array[i] = array[i] * 10;
        }
    }

    @Override
    public void printValue(Object value) {
        int[] array = (int[]) value;
        for (int entry : array) {
            System.out.printf("- %s\n", entry);
        }
    }
}
```


`Foo` is an API for using a value - it can create it, do something with it, and print it. `Bar` implements `Foo` with a list of strings that can be surrounded by parenthesizes, while `Baz` implements it with an array of integers that are multiplied by 10. There is a hidden assumption here that `handleValue` and `printValue` accept objects of the same type returned by `createValue` of the same `Foo` object:

```java
public class App {
    public static void main(String[] args) {
        Foo[] foos = new Foo[]{ new Bar(), new Baz() };
        for (Foo foo : foos) {
            Object value = foo.createValue();
            foo.handleValue(value);
            foo.printValue(value);
        }
    }
}
```

So - that assumption is easy to fulfill in actual code, but nothing forces it.

Proper OOP practices dictate that `handleValue` and `printValue` should be methods of the value:

```java
public interface Value {
    public void handle();
    public void print();
}

public interface Foo {
    public Value createValue();
}
```

Sadly, this is not always possible. Sometimes we don't control the types returned from `createValue`. Sometimes we do control them, but they belong in a different module that should not know about these particular operations.

# Generics to the rescue?

This is what generic are for, aren't they? Just make `Foo` use a generic parameter instead of `Object`:

```java
public interface Foo<T> {
    public T createValue();
    public void handleValue(T value);
    public void printValue(T value);
}

import java.util.LinkedList;
import java.util.List;

public class Bar implements Foo<List<String>> {
    @Override
    public List<String> createValue() {
        List<String> list = new LinkedList<>();
        list.add("one");
        list.add("two");
        list.add("three");
        return list;
    }

    @Override
    public void handleValue(List<String> value) {
        for (int i = 0; i < value.size(); ++i) {
            value.set(i, "(" + value.get(i) + ")");
        }
    }

    @Override
    public void printValue(List<String> value) {
        for (String entry : value) {
            System.out.printf("- %s\n", entry);
        }
    }
}

public class Baz implements Foo<int[]> {
    @Override
    public int[] createValue() {
        return new int[]{ 1, 2, 3 };
    }

    @Override
    public void handleValue(int[] value) {
        for (int i = 0; i < value.length; ++i) {
            value[i] = value[i] * 10;
        }
    }

    @Override
    public void printValue(int[] value) {
        for (int entry : value) {
            System.out.printf("- %s\n", entry);
        }
    }
}
```

So, we solved the problem of having to downcast in `handleValue` and `printValue`. So now we are typesafe, right?

WRONG!

We did not resolve the type issue - we have simply moved it from the declaration to the usage. When we loop over our array of `Foo`s we are still keeping the value from `createValue` as `Object`, and still passing it as `Object` to `handleValue` and `printValue`.

Java is kind enough to warn us about it:
```sh
$ javac App.java -Xlint:unchecked
App.java:6: warning: [unchecked] unchecked call to handleValue(T) as a member of the raw type Foo
            foo.handleValue(value);
                           ^
  where T is a type-variable:
    T extends Object declared in interface Foo
App.java:7: warning: [unchecked] unchecked call to printValue(T) as a member of the raw type Foo
            foo.printValue(value);
                          ^
  where T is a type-variable:
    T extends Object declared in interface Foo
2 warnings
```

But... what can we do about it?

# The solution

```java
public class App {
    public static void main(String[] args) {
        Foo[] foos = new Foo[]{ new Bar(), new Baz() };
        for (Foo<?> foo : foos) {
            createHandlePrint(foo);
        }
    }

    private static <T> void createHandlePrint(Foo<T> foo) {
        T value = foo.createValue();
        foo.handleValue(value);
        foo.printValue(value);
    }
}

```

I created an helper method - `createHandlePrint`. This method has a generic parameter, but it's caller doesn't pass it - neither explicitly nor implicitly. We only need it inside, to be be able to declare that `value` is the same type returned by the `createValue` of the `foo` passed to us, so it is safe to pass it back to that `foo`.

This is only possible thanks to type erasure. With real generics `createHandlePrint` would have been a different method for each parametrization of `T`, and I wouldn't be able to call them both from the same line of `main`. Unless I used reflection - which kind of defeats the purpose of having the compiler verify the type... (and also doesn't work with all implementations of generics)