Skip to content

Commit

Permalink
GenericsHierarchy also stores super class type parameters.
Browse files Browse the repository at this point in the history
closes #611
  • Loading branch information
NathanSweet committed Jul 12, 2018
1 parent afc7355 commit dbc7e6d
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 29 deletions.
83 changes: 56 additions & 27 deletions src/com/esotericsoftware/kryo/util/Generics.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.GenericDeclaration;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
Expand Down Expand Up @@ -182,40 +183,68 @@ static public class GenericsHierarchy {
final TypeVariable[] parameters;

public GenericsHierarchy (Class type) {
ArrayList<TypeVariable> temp = new ArrayList();
IntArray counts = new IntArray();
ArrayList<TypeVariable> parameters = new ArrayList();

TypeVariable[] params = type.getTypeParameters();
counts = new int[params.length];
int total = 0;
for (int i = 0, n = params.length; i < n; i++) {
TypeVariable param = params[i];
temp.add(param);
counts[i] = 1;

// If the parameter is passed to a super class, also store the super class type variable, recursively.
Class current = type;
while (true) {
Type genericSuper = current.getGenericSuperclass();
current = current.getSuperclass();
if (!(genericSuper instanceof ParameterizedType)) break;
TypeVariable[] superParams = current.getTypeParameters();
Type[] superArgs = ((ParameterizedType)genericSuper).getActualTypeArguments();
for (int ii = 0, nn = superArgs.length; ii < nn; ii++) {
Type superArg = superArgs[ii];
if (superArg == param) {
// We could skip if the super class doesn't use the type in a field.
param = superParams[ii];
temp.add(param);
counts[i]++;
Class current = type;
do {
TypeVariable[] params = current.getTypeParameters();
for (int i = 0, n = params.length; i < n; i++) {
TypeVariable param = params[i];
parameters.add(param);
counts.add(1);

// If the parameter is passed to a super class, also store the super class type variable, recursively.
Class currentSuper = current;
while (true) {
Type genericSuper = currentSuper.getGenericSuperclass();
currentSuper = currentSuper.getSuperclass();
if (!(genericSuper instanceof ParameterizedType)) break;
TypeVariable[] superParams = currentSuper.getTypeParameters();
Type[] superArgs = ((ParameterizedType)genericSuper).getActualTypeArguments();
for (int ii = 0, nn = superArgs.length; ii < nn; ii++) {
Type superArg = superArgs[ii];
if (superArg == param) {
// We could skip if the super class doesn't use the type in a field.
param = superParams[ii];
parameters.add(param);
counts.incr(counts.size - 1, 1);
}
}
}
}

total += counts[i];
}
total += counts.peek();
}
current = current.getSuperclass();
} while (current != null);

this.total = total;
parameters = temp.toArray(new TypeVariable[temp.size()]);
this.counts = counts.toArray();
this.parameters = parameters.toArray(new TypeVariable[parameters.size()]);
}

public String toString () {
StringBuilder buffer = new StringBuilder();
buffer.append("[");
int[] counts = this.counts;
TypeVariable[] parameters = this.parameters;
for (int i = 0, p = 0, n = counts.length; i < n; i++) {
int count = counts[i];
for (int nn = p + count; p < nn; p++) {
if (buffer.length() > 1) buffer.append(", ");
GenericDeclaration declaration = parameters[p].getGenericDeclaration();
if (declaration instanceof Class)
buffer.append(((Class)declaration).getSimpleName());
else
buffer.append(declaration);
buffer.append('<');
buffer.append(parameters[p].getName());
buffer.append('>');
}
}
buffer.append("]");
return buffer.toString();
}
}

Expand Down
41 changes: 39 additions & 2 deletions test/com/esotericsoftware/kryo/serializers/GenericsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
package com.esotericsoftware.kryo.serializers;

import com.esotericsoftware.kryo.KryoTestCase;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;

import java.io.Serializable;
Expand All @@ -29,7 +30,6 @@

import org.junit.Before;
import org.junit.Test;
import org.objenesis.strategy.StdInstantiatorStrategy;

public class GenericsTest extends KryoTestCase {
{
Expand Down Expand Up @@ -80,6 +80,24 @@ public void testDifferentTypeArguments () {
kryo.writeClassAndObject(buffer, o2);
}

// https://github.com/EsotericSoftware/kryo/issues/611
@Test
public void testSuperGenerics () {
kryo.setReferences(false);
kryo.register(SuperGenerics.Root.class);
kryo.register(SuperGenerics.Value.class);

Output output = new Output(2048, -1);

SuperGenerics.Root root = new SuperGenerics.Root();
root.rootSuperField = new SuperGenerics.Value();
kryo.writeObject(output, root);
output.flush();

Input input = new Input(output.getBuffer(), 0, output.position());
SuperGenerics.Root root2 = kryo.readObject(input, SuperGenerics.Root.class);
}

private interface Holder<V> {
V getValue ();
}
Expand Down Expand Up @@ -139,7 +157,6 @@ public boolean equals (Object obj) {
}

static private class BaseGeneric<T extends Serializable> {

// The type of this field cannot be derived from the context.
// Therefore, Kryo should consider it to be Object.
private final List<T> listPayload;
Expand Down Expand Up @@ -206,4 +223,24 @@ public ConcreteClass (final List listPayload) {
super(listPayload);
}
}

static public class SuperGenerics {
static public class RootSuper<RS> {
public ValueSuper<RS> rootSuperField;
}

static public class Root extends RootSuper<String> {
}

static public class ValueSuper<VS> extends ValueSuperSuper<Integer> {
VS superField;
}

static public class ValueSuperSuper<VSS> {
VSS superSuperField;
}

static public class Value extends ValueSuper<String> {
}
}
}

0 comments on commit dbc7e6d

Please sign in to comment.