Accessing functions from Java Generics

Hi!
Recently I’ve been making some classes to carry out functions, but when making them, I’ve only been able to use 1 class for one object. For example,

class Sorter {
  Point[] points;
  Sorter(Point[] points_) {
    points = points_;
  }
  void sort() {
    <Sort function... (Accesses variable from points such as points[x].x)>
  }
}

Right now, the class sorter can only sort points, but I would like it to be able to sort any object. I’ve tried using Java Generics, but I couldn’t find a way to access functions from it (Eg. class Sorter<T>, I can’t access T.x or any variables T may have).

I know I can just check the generic type with instanceof, but, is there a easier way I can use generics to do it, or an alternative way? I’m not sure how well I’ve explained this, so if you have any questions, feel free to ask!

Thanks! :slight_smile:

If you intend to sort() your arrays or collections by some numerical value, you can make your classes implements DoubleSupplier:

import java.util.function.DoubleSupplier;

class SomeClass implements DoubleSupplier {
  float x;

  @Override double getAsDouble() {
    return x;
  }
}

And use the Comparator below in order to sort() by ascending numerical order:

import java.util.Comparator;
import java.util.function.DoubleSupplier;

import java.util.Arrays;
import java.util.Collections;

static final Comparator<DoubleSupplier> ASCENT = new Comparator<DoubleSupplier>() {
  @Override final int compare(final DoubleSupplier d1, final DoubleSupplier d2) {
    return (int) Math.signum(d1.getAsDouble() - d2.getAsDouble());
  }
};
  1. Docs.Oracle.com/javase/10/docs/api/java/util/Arrays.html#sort(T[],java.util.Comparator)
  2. Docs.Oracle.com/javase/10/docs/api/java/util/Collections.html#sort(java.util.List,java.util.Comparator)

No idea why you’d make your classes implement DoubleSupplier. The normal way to do this would be to make the classes implement Comparable (if you’re writing them all? ). If you change your generic definition to then be <T extends Comparable> you can access the interface method. Of course, you don’t really need to because sort is already available for arrays and collections of items using Comparable.

https://docs.oracle.com/javase/7/docs/api/java/lang/Comparable.html

Sorry if I didn’t make it clear, I’m using Sorter as an example right now. What I’m trying to do is to find out how to do it, such as if I wanted a QuadTree to be able to handle any types of objects.

Can you be more specific what “handle” means there? If you want to access fields/methods other than those inherited from Object then you need to have a common type with this field/method they inherit from using the “T extends” syntax I posted earlier. Otherwise your only option is runtime checking with instanceof and casting.

By “Handle”, I mean I want to be able to access <T> and all of its functions. Eg. If I declare <T> as a PVector, I want to be able to access the PVector's x and y

This is not how generics work! The only way you could do that with generics is if you had <T extends PVector> but then you would only be able to put PVectors in it. Or you make specific subclasses to handle specific types.

Yep :neutral_face:, that’s where I got stuck. Would the only way to have the class be able to “handle” would be to take in object o and check with instanceof what class it was?

Even If we had 2 or more classes w/ the exactly same code, Java would still “see” them as different from each:

void setup() {
  MyVec vec1 = new MyVec();
  MyVecTwin vec2 = new MyVecTwin();

  println(MyVec.class.isInstance(vec1)); // true
  println(MyVec.class.isInstance(vec2)); // false

  exit();
}

class MyVec {
  float x = random(-100, 100), y = random(-100, 100), z;

  @Override String toString() {
    return "[ " + x + ", " + y + ", " + z + " ]";
  }
}

class MyVecTwin { // exactly same code as class MyVec
  float x = random(-100, 100), y = random(-100, 100), z;

  @Override String toString() {
    return "[ " + x + ", " + y + ", " + z + " ]";
  }
}

So it doesn’t matter if a class got structurally the same members as another 1; under Java’s PoV, they’re not the same! :crazy_face:

In my previous reply, I’ve instantiated a Comparator which can be used to sort() any array or list of objects which implements the DoubleSupplier datatype.

If we needed to sort() a container of PVector objects instead, we’d need to change the Comparator’s generic datatype DoubleSupplier to PVector.

Plus, switch method DoubleSupplier::getAsDouble() to field PVector::x inside method Comparator::compare()'s implementation:

import java.util.Comparator;
import java.util.Arrays;
//import java.util.Collections;

static final Comparator<PVector> ASCENT_X = new Comparator<PVector>() {
  @Override final int compare(final PVector v1, final PVector v2) {
    return (int) Math.signum(v1.x - v2.x);
  }
};

Still, we couldn’t sort() neither an array of MyVec nor of MyVecTwin w/ the Comparator<PVector> above; even though they’ve both got the field x in common, just like a PVector: :disappointed:

import java.util.Comparator;
import java.util.Arrays;
//import java.util.Collections;

static final Comparator<PVector> ASCENT_X = new Comparator<PVector>() {
  @Override final int compare(final PVector v1, final PVector v2) {
    return (int) Math.signum(v1.x - v2.x);
  }
};

final PVector[] vecs = {
  PVector.random2D(this).mult(100), 
  PVector.random2D(this).mult(100), 
  PVector.random2D(this).mult(100)
};

final MyVec[] myVecs = { new MyVec(), new MyVec(), new MyVec() };

class MyVec {
  float x = random(-100, 100), y = random(-100, 100), z;

  @Override String toString() {
    return "[ " + x + ", " + y + ", " + z + " ]";
  }
}

void setup() {
  println(vecs);
  Arrays.sort(vecs, ASCENT_X);
  println(vecs);
  println();

  println(myVecs);
  //Arrays.sort(myVecs, ASCENT_X); // Error: MyVec isn't a PVector!!!
  //println(myVecs);

  exit();
}

That’s too bad that a class can only be used for 1 object type. :slightly_frowning_face: Thanks for all the examples you did, though! Since I use to use P5.js, a class could take in any object type, so I assumed Processing would be able to do the same thing then. In that case, I’ve decided to just do something like this (Using Sorter as an example):

class Sorter {
  Object[] points;
  Sorter(Object[] points_) {
    if (points_ instanceof Point) points = new Point[];
    if (points_ instanceof PVector) points = new PVector[];
    etc etc...
  }
  void sort() {
    <Sort function... (Accesses variable from points such as points[x].x)>
  }
}

Would that be the most efficent way to do it?

Ideally, no! You’d write a subclass specific to the type you want to handle, while keeping all the generic stuff (pun intended!) in the superclass eg.

class PVectorQuadTree extends QuadTree<PVector> {
  // methods using T as PVector here 
  // eg. anything returning T in superclass will give you a PVector
  // anything accepting a T can be overridden to accept PVector
}

There are other ways, eg. by accepting a Consumer<T> in a method you will then be able to pass in a Consumer<PVector> to a QuadTree<PVector> This is how the forEach methods on Java 8 collections work, but it’s more awkward to use without lambdas - see https://docs.oracle.com/javase/8/docs/api/java/lang/Iterable.html#forEach-java.util.function.Consumer-

Thanks for your quick reply! I’ll try out using children classes, but overall I would like to use 1 class to keep it simple. I think my question is answered for now; Thanks to everyone who helped out!

Is that Point some class which you’d implemented by yourself? :question:
If so, you can simply declare that it extends PVector: :shushing_face:

class Point extends PVector {
  Point() {
  }

  Point(final float x, final float y) {
    this(x, y, 0);
  }

  Point(final float x, final float y, final float z) {
    super(x, y, z);
  }
}

Now, the same sort() function can accept both the superclass PVector or its subclass Point transparently. :high_brightness:

In case you can’t make your class Point extends class PVector, or even prefer not to, you can instead code your function under Java’s built-in JavaScript engine called Nashorn! :coffee:

This way, you’ll be able to pass an object of any datatype to that JS function, as long as that object got all of the members your function expects to find in it. :star_struck:

I’ve re-written the previous Java Comparator ASCENT_X:

static final Comparator<PVector> ASCENT_X = new Comparator<PVector>() {
  @Override final int compare(final PVector v1, final PVector v2) {
    return (int) Math.signum(v1.x - v2.x);
  }
};

in Nashorn JS syntax now: :rhinoceros:

final Comparator ASCENT_X = (Comparator) runJS(compileJS(js, 
  "new java.util.Comparator() {", 
  "  compare: function (obj1, obj2) {", 
  "    return obj1.x - obj2.x;", 
  "  }", 
  "};"));

Here’s the whole sketch, which is now able to sort() any object datatype which got field x in it, bypassing Java’s strict datatype system: :innocent:

/**
 * JS Nashorn Comparator (v1.2)
 * GoToLoop (2018/Sep/10)
 * Discourse.Processing.org/t/accessing-functions-from-java-generics/3391/14
 */

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

import javax.script.CompiledScript;
import javax.script.Compilable;

import java.util.Comparator;
import java.util.Arrays;
//import java.util.Collections;

final ScriptEngine js = new ScriptEngineManager().getEngineByName("Nashorn");

final Comparator ASCENT_X = (Comparator) runJS(compileJS(js, 
  "new java.util.Comparator {", 
  "  compare: function (obj1, obj2)  obj1.x - obj2.x", 
  "};"));

final PVector[] vecs = {
  PVector.random2D(this).mult(100), 
  PVector.random2D(this).mult(100), 
  PVector.random2D(this).mult(100)
};

final MyVec[] myVecs = { new MyVec(), new MyVec(), new MyVec() };

public class MyVec {
  public float x = random(-100, 100), y = random(-100, 100), z;

  @Override String toString() {
    return "[ " + x + ", " + y + ", " + z + " ]";
  }
}

void setup() {
  println(ASCENT_X, ENTER);

  println(vecs);
  Arrays.sort(vecs, ASCENT_X);
  println(vecs);
  println();

  println(myVecs);
  Arrays.sort(myVecs, ASCENT_X);
  println(myVecs);

  exit();
}

@SafeVarargs static final CompiledScript compileJS
  (final ScriptEngine js, final String... statements) {
  final String expression = statements != null?
    PApplet.join(statements, PConstants.ENTER) : "";

  try {
    return ((Compilable) js).compile(expression);
  }
  catch (final ScriptException cause) {
    PApplet.println(cause);
    System.err.println(expression);
    throw new RuntimeException(cause);
  }
}

static final Object runJS
  (final CompiledScript compiled) {
  try {
    return compiled.eval();
  }
  catch (final ScriptException cause) {
    PApplet.println(cause);
    throw new RuntimeException(cause);
  }
}
1 Like

Good idea! Just to clarify, I’m making a game with a bunch of entities, and I need grids for class Player and Enemy. I think I’m going to make Player and Enemy extend off class Entity, and have class Grid take in class Entity.

Wow, thanks so much! :slight_smile: Is that code just Java to ignore any other errors? (That could potentially mess up the program)

This is the right approach, and anything generic can be <T extends Entity> if you need it.

Using Nashorn is a bad idea for this (crossing language barriers comes with a cost), and it’s also in the process of being dropped from the JDK so your code might not work in future.

1 Like

Attention: “JS Nashorn Comparator” Version 1.0 had an unforeseen bug! :warning:

Apparently, Nashorn can’t “see” objects’ non-public members! :ghost:

Therefore, both the class MyVec and its fields x, y, z had to be explicitly declared as public in order for Nashorn’s code to access them. :roll_eyes:

Otherwise, the field x would be undefined inside Nashorn’s callback function! :grimacing:

Issue is fixed now under version 1.1! :space_invader:

1 Like

Alright, I’m going to get started now! Thanks everyone for your help, it’s been very helpful! :slight_smile: