5

I am trying to simulate a kind of pointer used in another obscure programming paradigm, so I can port some code to Java. The other language is not object-oriented, and was loosely inspired by Pascal.

In the original language, we can write code like this. First, working with text.

// Start with text.
Text myVar = "Bonjour" 
Pointer myPointer = ->myVar       // Referencing a string variable, storing the reference in another variable of type `Pointer`.
Message( myPointer-> )    // Dereferencing the pointer, to retrieve `myVar`, and pass the string to a command `Display` that displays the message on screen in a dialog box.

Then, switching to numbers.

// Switch gears, to work with an number.
Integer vResult = ( Random % ( vEnd - vStart + 1 ) ) + vStart  // Generate random number.
myPointer = ->vResult    // The same pointer now points to numeric variable rather than a textual variable. 

We can assign a pointer by the text of a variable name.

myPointer = Get pointer( "var" + String($i) ) // Generate pointer variable named `var1`, or `var2`, etc.

We can ask the pointer for a code number representing the data type of the value to which it is pointing (the data type of the referent).

typeCodeNumber = Type( myPointer ) // Returns 11 for an integer, 22 for text.

In this other language, the compiler does provide for type-safety. But when using pointers in this fashion, we sacrifice type-safety. The compiler emits a warning that the code usage is ambiguous with regard to type.

My idea to port this code is to define a XPointer class as well as classes for the types such as XText and XInteger.

I need to hold a reference to an object of any of a dozen specific known types, including to another pointer. I can hard-code the dozen types, no need to be open to all types.

These dozen types do not share an interface nor abstract class other than Object. And even if they did share an interface/superclass, I do not want them returned as a superclass but as their original concrete class. As they entered the pointer, so should they emerge from the pointer.

My current plan is to define a XPointer class in Java with a pair of reference and dereference methods:

  • XPointer::ref( x ) where you pass an object of Dog, Truck, or Sculpture class, or even another XPointer object.
  • XPointer::deref ⇒ x where x is an object recognized as its original type, a Dog, a Truck, or a Sculpture or even another XPointer object, rather than a mere Object object.

➥ Is there some way to do this Java? Perhaps with Generics?

➥ If not possible in Java, I could reluctantly switch to Kotlin. Can this pointer functionality can be done in Kotlin running on a JVM?

So code my look like this:

XPointer p = new XPointer() ;  // Points to nothing, null.

p.ref( new Dog() ) ;           // Pointer points to a `Dog` object.
p.deref().bark() ;             // Pointer can retrieve the `Dog` as such, a `Dog` object.

p.ref( someTruck ) ;           // The pointer can switch to pointing to an object of an entirely different type. The `Dog` object has been replaced by a `Truck` object.
p.deref().honk() ;             // Dereference the stored `Truck` object as such.

And a pointer to a pointer.

XPointer p2 = new XPointer() ; // Points to nothing, null.
p2.ref( p ) ;                  // 2nd pointer points to a pointer that points to a `Truck` object.
p2.deref().deref().honk() ;    // Dereference the stored `Truck` object as such.

If there is a better route towards such a pointer-simulation, I am open to suggestions. Elegance is not required; any hack will do.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • [`Optional`](https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html) good enough? Or maybe a [`Deque`](https://docs.oracle.com/javase/8/docs/api/java/util/Deque.html). – Elliott Frisch Feb 25 '19 at 06:18
  • @ElliottFrisch I am familiar with `Optional` for signaling a [possible null in a return value](https://stackoverflow.com/a/26328555/642706). But I cannot see how it applies here? How might a `Pointer::ref` method look? – Basil Bourque Feb 25 '19 at 06:20
  • actually an array with one field resembles a pointer. – kai Feb 25 '19 at 06:55
  • @kai But an array must have a specific type. How to return the object as its original concrete type? Notice in my example code how I switched from storing an `Dog` to storing a `Truck` in the same pointer, yet was able to return the dog as a `Dog` and the truck as a `Truck`. – Basil Bourque Feb 25 '19 at 06:59
  • an array has a type in Java it is YourType[] ptr = {yourobject}; That is equivalent to C: YourType * ptr = &yourobject; – kai Feb 25 '19 at 07:06
  • Are you sure you need exactly pointer simulation? If you cannot add a common superclass souds like typeclasses would be a way to go. I mean whenever you need to use one of your dozens types you can introduce a type parameter with context boundaries specifying operations you can do with your types – Some Name Feb 25 '19 at 07:31
  • In Scala this is definitely possible via context boundaries and implicit conversions. I'm not familar with Kotlin unfortunately. – Some Name Feb 25 '19 at 07:36
  • What is the precise use case for this? I would just use a concrete object. An Object reference is precisely a pointer, it's just part of the language. you need to know the type anyway to dereference in java as well as C, so I would just use a getter to get the reference of the child object. if you do need union type like things you need to have a getter for each of them and some kind of liveness check (does the value exist). – Alexander Oh Feb 25 '19 at 11:18
  • @Alex I added more details. You make a good point about the programmer in this other language being responsible for knowing the data type and the content of the pointer’s referent. When using pointers in this other language, we sacrifice type-safety. To aid the programmer in managing the ambiguity, the language provides commands for functionality like identifying the data type of the referent, and indicating if the pointer is currently pointing to a referent (is the pointer nil or not). My solution may lie in there somewhere. My code translator may need to add casts according to the context. – Basil Bourque Feb 25 '19 at 22:50

3 Answers3

2

This is known as a union or a variant type (see Boost.Variant in C++). The latter is specially handy as it's a type-safe container for a heterogeneous set of types and stands the closest to your description of a type of code you are porting from. Since Java does not support templates - no, generics are not templates - you won't get exactly what you are looking for.

The Optional type is the simplest case of two types: a type T and a Null. Given your requirement to store more than a type T or Null, it won't work for you.

You may want to check out the JavaSealedUnions. In addition, Kotlin provides a concept of sealed classes, helpful to limit a value to one of the types from a constrained set.

Good luck!

Slawomir
  • 3,194
  • 1
  • 30
  • 36
  • I don't think that union types have that kind of behaviour. – Alexander Oh Feb 25 '19 at 16:40
  • I was referring to one of the requirements stated by the OP, specifically the "need to hold a reference to an object of any of a dozen specific known types". – Slawomir Feb 26 '19 at 04:41
0

My apologies for the long answer.
I don't think you can achieve exactly what you want with Generics without losing type safety.

For the next line to work, Java needs to know there is a Truck returned by the defer() method so it can call honk() on that object.

p.deref().honk();

But if you would use Java Generics, than that infers type erasure on compilation.
The solution would imply adding a generic type like Pointer<Truck>, but we can't since you want to be able to add a dozen specific known types and a Pointer.

However ... since you yourself write that line like that, specifically making a call to bark() or honk(),
it implies you already know that the Pointer references a Dog or Truck object at that point in your code.

Using that assumption, Generics and a modification to defer(), you can get pretty close to a solution.

Let's say we have these classes, which have no links in any way.

public class Dog {
    public void bark() {
        System.out.println("Bark ...");
    }
}

public class Truck {
    public void honk() {
        System.out.println("Honk ...");
    }
}

Then we need a Pointer class like this, where the deref() method needs a Class parameter to know which type to return.

public class Pointer {

    private Object myObj;

    public <T> void ref(T myObject) {
        this.myObj = myObject;
    }

    public <T> T deref(Class<T> myClazz) {
        try {
            return myClazz.cast(myObj);
        } catch(ClassCastException e) {
            return null;
        }
    }
}

Then you can do the following

public static void main(String[] args) {
    Pointer pointer = new Pointer();

    Dog dog = new Dog();
    pointer.ref(dog);                  // Reference to a Dog
    pointer.deref(Dog.class).bark();

    Truck truck = new Truck();
    pointer.ref(truck);                // Reference to a Truck
    pointer.deref(Truck.class).honk();

    Pointer subPointer = new Pointer();
    pointer.ref(subPointer);           // Reference to another pointer
    subPointer.ref(truck);             // Other pointer references a Truck
    pointer.deref(Pointer.class).deref(Truck.class).honk();
    subPointer.ref(dog);               // Other pointer references a Dog
    pointer.deref(Pointer.class).deref(Dog.class).bark();

    pointer.ref(null);                 // Clears the reference

    Truck bulldog = new Truck();
    pointer.ref(bulldog);
    pointer.deref(Dog.class).bark();   // Is allowed, but will cause a NullPointerException
}

This will print out:

Bark ...
Honk ...
Honk ...
Bark ...
Exception in thread "main" java.lang.NullPointerException at Pointer.main(Pointer.java:39)


If you need to limit your Pointer reference to only your dozen specific known types and the Pointer class itself, then you need to extend them with a super class (for example Pointerable) and modify the ref() method accordingly.
Then you can prevent types that don't extend this super class to be referenced by your Pointer.
Example:
public abstract class Pointerable { }

public class Dog extends Pointerable {
    public void bark() {
        System.out.println("Bark ...");
    }
}

public class Pointer extends Pointerable {

    private Object myObj;

    public <T extends Pointerable> void ref(T myObject) {
        this.myObj = myObject;
    }

    public <T extends Pointerable> T deref(Class<T> myClazz) {
        try {
            return myClazz.cast(myObj);
        } catch(ClassCastException e) {
            return null;
        }
    }
}
George Derpi
  • 668
  • 4
  • 10
0

This is the best I can come up with. I think you can't easily make a common Type, without it being the parent of all classes (like Object). Also conversion is hard given the type system, you can possibly mimic it by not allowing to reassign pointers of different type, but just generating a new pointer every time you do a get. if you don't like to know the type itself you can use var in modern java to hide the type difference.

public class Types {
    public static void main(String[] args) {
        var p = new PascalPointer<>(new PascalInt());
        PascalInt i = p.get();
        var p2 = new PascalPointer<>(i.toPStr()); // mimic generics by type inference.
        PascalString s = p2.get();
        PascalPointer<PascalType> genericPointer = new PascalPointer<>(s);
        genericPointer.set(i);
        var i2 = (PascalInt) genericPointer.get();
    }
    public interface PascalType { }

    public static class PascalInt implements PascalType {
        // cast
        public PascalString toPStr() {
            return new PascalString(); // hide a new to do a conversion instead of a cast really.
        }
    }

    public static class PascalString implements PascalType { }

    public static class PascalPointer<T extends PascalType> {
        T t;
        public PascalPointer(T t) { this.t = t; }
        public T get() { return t;}
        public void set(T t) {this.t = t;}
    }

}

This probably breaks down when you want to have an Int view of a string and you update the string (as I am copying here).

Alexander Oh
  • 24,223
  • 14
  • 73
  • 76