用Java模拟指针?在科特林吗?

时间:2019-02-25 06:16:50

标签: java pointers types

我正在尝试模拟另一种晦涩的编程范例中使用的一种指针,因此可以将一些代码移植到Java。另一种语言不是面向对象的,并且受到Pascal的宽松启发。

在原始语言中,我们可以编写这样的代码。首先,处理文本。

// 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.

然后,切换到数字。

// 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. 

我们可以通过变量名的文本来分配指针。

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

我们可以要求指针提供一个代表其所指向的值的数据类型(指代对象的数据类型)的代码号。

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

使用另一种语言,编译器确实为type-safety提供了{em> 。但是当以这种方式使用指针时,我们牺牲了类型安全性。编译器发出警告,指出代码使用与类型有关。

我移植此代码的想法是定义一个XPointer类以及XTextXInteger之类的类型。

我需要保持对所有已知特定类型的对象的引用,包括对另一个指针的引用。我可以对十二种类型进行硬编码,而无需对所有类型都开放。

除了Object以外,这十几种类型不共享任何接口或抽象类。即使它们确实共享一个接口/超类,我也不希望它们作为超类返回,而是作为其原始的具体类返回。当它们进入指针时,它们也应该从指针中出现。

我目前的计划是在Java中使用一对 reference dereference 方法定义一个XPointer类:

  • XPointer::ref( x ),您可以在其中传递DogTruckSculpture类的对象,甚至是另一个XPointer对象。
  • XPointer::deref ⇒ x,其中x是被识别为其原始类型的对象,是DogTruckSculpture甚至另一个XPointer对象,而不是单纯的Object对象。

➥有什么方法可以做到这一点?也许使用Generics

➥如果用Java无法实现,我可能会不情愿地切换到Kotlin。可以在运行于KotlinJVM中完成此指针功能吗?

因此,将我的代码编写如下:

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.

还有一个指向指针的指针。

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.

如果有更好的方法来进行这种指针模拟,我欢迎提出建议。不需要优雅;任何黑客都会这么做。

3 个答案:

答案 0 :(得分:2)

这称为 union variant 类型(请参见C ++中的Boost.Variant)。后者特别方便,因为它是用于异构类型集的类型安全容器,并且最接近您要从中移植的代码类型的描述。由于Java不支持模板-no, generics are not templates-您将无法获得所需的确切信息。

Optional 类型是两种类型中最简单的情况:类型T和Null。考虑到您需要存储T型或Null类型以上的存储,因此对您不起作用。

您可能想签出JavaSealedUnions。另外,Kotlin提供了sealed classes的概念,有助于将值限制为约束集中的一种类型。

祝你好运!

答案 1 :(得分:0)

很抱歉我很抱歉。
我认为您不能在不损失类型安全的情况下使用泛型完全实现您想要的。

为了使下一行工作,Java需要知道Truck方法返回的defer()才可以在该对象上调用honk()

p.deref().honk();

但是,如果您要使用Java泛型,则可以推断出编译时的类型擦除。
该解决方案将暗示添加诸如Pointer<Truck>之类的通用类型,但是由于您希望能够添加一打特定的已知类型和一个Pointer,所以我们不能这样做。

但是... ,因为您自己这样写那行,尤其是致电bark()honk()
这意味着您已经知道Pointer在代码中的那个位置引用了DogTruck对象。

使用该假设,泛型和对defer()的修改,您将很接近解决方案。

比方说我们有这些类,它们没有任何形式的链接。

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

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

然后,我们需要一个类似Pointer的类,其中deref()方法需要一个Class参数来知道要返回哪种类型。

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;
        }
    }
}

然后您可以执行以下操作

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
}

这将打印出来:

  

树皮...
  鸣笛...
  鸣笛...
  树皮...
  Pointer.main(Pointer.java:39)的线程“ main”中的异常java.lang.NullPointerException


如果您只需要将Pointer引用限制为您的特定于特定类型的数Pointer类本身,则需要使用超类扩展它们(例如{ {1}})并相应地修改Pointerable方法。
然后,可以防止指针不能引用没有扩展此超类的类型。
示例:

ref()

答案 2 :(得分:0)

这是我能想到的最好的方法。我认为,如果不成为所有类(例如Object)的父类,就不能轻易创建一个通用Type。 在给定类型系统的情况下,转换也很困难,您可以通过不允许重新分配不同类型的指针来模仿它,而每次执行获取操作时仅生成一个新指针即可。如果您不希望知道类型本身,则可以在现代Java中使用var来隐藏类型差异。

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;}
    }

}

当您想要一个字符串的Int视图并更新该字符串时(如我在此处复制的内容),这可能会崩溃。