【问题标题】:Simulate a pointer with Java? In Kotlin?用Java模拟指针?在科特林?
【发布时间】:2019-07-18 12:25:44
【问题描述】:

我正在尝试模拟另一种晦涩的编程范例中使用的一种指针,因此我可以将一些代码移植到 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。但是当以这种方式使用指针时,我们牺牲了类型安全。编译器会发出警告,指出代码使用在类型方面不明确。

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

我需要持有一个对十几种特定已知类型中任何一种的对象的引用,包括对另一个指针的引用。我可以硬编码十几种类型,不需要对所有类型开放。

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

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

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

➥ 有没有办法做到这一点Java?也许是Generics

➥ 如果在 Java 中不可能,我可以不情愿地切换到 Kotlin。这个指针功能可以在Kotlin 上运行在JVM 上完成吗?

所以我的代码是这样的:

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.

如果有更好的方法实现这种指针模拟,我愿意接受建议。不需要优雅;任何黑客都可以。

【问题讨论】:

  • Optional<T> 够好吗?或者可能是Deque<T>
  • @ElliottFrisch 我熟悉Optional 用于发出possible null in a return value 的信号。但我看不到它在这里如何应用? Pointer::ref 方法看起来如何?
  • 其实一个字段的数组就像一个指针。
  • @kai 但是数组必须有特定的类型。如何将对象作为其原始具体类型返回?请注意,在我的示例代码中,我如何从在同一指针中存储 Dog 切换到存储 Truck,但能够将狗返回为 Dog,将卡车返回为 Truck
  • 一个数组在 Java 中有一个类型,它是 YourType[] ptr = {yourobject};这相当于 C: YourType * ptr = &yourobject;

标签: java pointers types


【解决方案1】:

这称为 unionvariant 类型(参见 C++ 中的 Boost.Variant)。后者特别方便,因为它是一组异构类型的类型安全容器,并且最接近您对要移植的代码类型的描述。由于 Java 不支持模板 - no, generics are not templates - 你不会得到你想要的。

Optional 类型是两种类型中最简单的情况:类型 T 和 Null。鉴于您要求存储的类型不只是 T 或 Null,它对您不起作用。

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

祝你好运!

【讨论】:

  • 我不认为联合类型有这种行为。
  • 我指的是 OP 提出的要求之一,特别是“需要持有对十几种特定已知类型中任何一种的对象的引用”。
【解决方案2】:

对于冗长的回答,我深表歉意。
我不认为你可以在不失去类型安全的情况下使用泛型实现你想要的。

为了使下一行工作,Java 需要知道defer() 方法返回了一个Truck,以便它可以在该对象上调用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 类本身,那么您需要使用超类(例如Pointerable)扩展它们并且相应地修改ref() 方法。
然后,您可以防止指针引用不扩展此超类的类型。
例子:
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;
        }
    }
}

【讨论】:

    【解决方案3】:

    这是我能想到的最好的。我认为你不能轻易地创建一个通用类型,而不是它是所有类的父类(如对象)。 考虑到类型系统,转换也很困难,您可以通过不允许重新分配不同类型的指针来模仿它,但每次执行获取时只生成一个新指针。如果您不想知道类型本身,您可以在现代 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 视图并更新字符串时,这可能会发生故障(正如我在此处复制的那样)。

    【讨论】:

    • 你可以对 Optional 做同样的事情。
    猜你喜欢
    • 1970-01-01
    • 2021-05-11
    • 2021-04-05
    • 1970-01-01
    • 1970-01-01
    • 2021-11-28
    • 1970-01-01
    • 1970-01-01
    • 2022-11-16
    相关资源
    最近更新 更多