【问题标题】:When building containers why is using Java Generics better than using the Object Class? (Java Generics & DataStructures)在构建容器时,为什么使用 Java 泛型比使用对象类更好? (Java 泛型和数据结构)
【发布时间】:2019-10-13 10:05:46
【问题描述】:

所以我一直在审查我的数据结构,并遇到了一个关于 Java 泛型和 Object 类的有趣想法。我已经以两种不同的方式实现和运行“通用包”(注意以下:IObjectBag.java、ObjectBag.java、IGenericBag.java 和 GenericBag.java),并且都使用了它们 (注意:在 main.java 和输出下面)。我已经根据堆栈溢出规则删除了一些不必要的代码,但如果您想要完整的实现,请告诉我。

此外,除了查看ArrayListhere 的源代码之外,我还在许多网站、书籍和课程中研究过这个主题,我知道我的GenericBag 比我的@987654325 更好。 @ 但还不足以在面试中以实际的方式解释它。我很困惑,我的GenericBag 在其实现中比我的ObjectBag 使用了更多的转换操作(请参阅RemovePrintBag)。

  1. 那么,除了语法糖,为什么我的GenericBag 更好?请以我的课程为例。

  2. 在运行时间/开销/空间/时间方面有什么我没有注意到的重要差异吗?

  3. 你会如何回答这个问题,或者希望它在面试中得到回答?

奖励问题:如果您愿意,请在MainGenericBag cmets 中回答奖励问题(我想我可以自己回答,只是想听听您的意见)。

IObjectBag接口:

public interface IObjectBag {
    void add(Object item);
    Object remove(Object item) throws NoSuchElementException;
    boolean isEmpty();
    int find(Object item);
    Object get(int index);
    int numItems();
}

ObjectBag类:

public class ObjectBag implements IObjectBag {
    private Object [] items; // the java class attribute that will hold out "ints"
    private int numItems;
    
    public static void printBag(IObjectBag bag) {
        for(int i = 0; i < bag.numItems(); i++) {
            System.out.println(bag.get(i));
        }
    }
    
    public ObjectBag(int size) {
        this.items = new Object[size]; // fills array with null values
        this.numItems = 0;
    }
    
    public void add(Object item){
        // adds item to end of bag
    }
    
    public Object remove(Object item) {
        int index = this.find(item);

        if(index == -1) throw new NoSuchElementException("oops nothing found");
        
        Object out = this.items[index];
        
        this.items[index] = null;
        this.numItems -= 1;
        
        if(index + 1 != this.items.length && this.items[index + 1] != null) {  
            for(int i = index; i < this.items.length; i++) {
                if(i + 1 != this.items.length) this.items[i] = this.items[i + 1];
            }
            
            this.items[this.items.length - 1] = null;
        }
        
        return out;
    }
    
    public int find(Object item) {
        // return index given item or -1 
    }
    
    public Object get(int index) {
        // returns item given index
    }
    
}

IGenericBag类:

public interface IGenericBag <T> {
    void add(T item);
    T remove(T item) throws NoSuchElementException;
    boolean isEmpty();
    int find(T item);
    T get(int index);
}

GenericBag类:

public class GenericBag<T> implements IGenericBag<T> {
    // private T[] items; can't use this b/c see comment in constructor
    private Object[] items;
    private int numItems;
    
    public static void printBag(GenericBag bag) {
        for(int i = 0; i < bag.numItems(); i++) {
            System.out.println(bag.get(i));
        }
    }
    
    public GenericBag(int size) {
        // this.items = new T[size]; Bonus: throws generic array creation error (why?)
        this.items = new Object[size];
        this.numItems = 0;
    }
    
    public void add(T item){
        this.items[this.numItems] = item;
        this.numItems += 1;
    }
    
    public T remove(T item) {
        int index = this.find(item);

        if(index == -1) throw new NoSuchElementException("oops nothing found");
        
        T out = (T) this.items[index];
        
        this.items[index] = null;
        this.numItems -= 1;
        
        if(index + 1 != this.items.length && this.items[index + 1] != null) {  
            for(int i = index; i < this.items.length; i++) {
                if(i + 1 != this.items.length) this.items[i] = this.items[i + 1];
            }
            
            this.items[this.items.length - 1] = null;
        }
        
        return out;
    }
    
    public int find(Object item) {
        // given object return index or throw exception
    }
    
    public T get(int index) {
        return (T) this.items[index];
    }
    
}

Main类:

public class Main {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        System.out.println("Hello StackOverFlow!");
        Object int1 = new Integer(1);
        Object int2 = new Integer(2);
        Object int3 = new Integer(3);
        
        
        /* using my object bag ************************************************/
        System.out.println("using my object bag");
        IObjectBag myObjectBag = new ObjectBag(3);
                
        myObjectBag.add(int1);
        myObjectBag.add(int2);
        myObjectBag.add(int3);
        myObjectBag.remove(int2);
        
        ObjectBag.printBag(myObjectBag);
        
        /* using my generic bag ***********************************************/
        System.out.println("using generic bag");
        
        
        // Bonus Question: using object like above causes error at add method (why?)
        Integer int4 = new Integer(4); 
        Integer int5 = new Integer(5);
        Integer int6 = new Integer(6);
        
        GenericBag<Integer> myGenericBag = new GenericBag<Integer>(3); 
        //Bonus Question: using Interface decllaration like above causes error in print bag (why?) 
        
        myGenericBag.add(int4);
        myGenericBag.add(int5);
        myGenericBag.add(int6);
        myGenericBag.remove(int4);
        
        GenericBag.printBag(myGenericBag);
    }
    
}

输出:

Hello StackOverFlow!
using my object bag
1
3
using generic bag
5
6

【问题讨论】:

    标签: java generics containers type-safety collect


    【解决方案1】:

    由您的 GenericBag 实现提供的类型安全“自动”解决的 ObjectBag 问题:

    • 访问条目返回Object,在这个阶段你不知道Object是什么类型。
    • 您可以将任何类型的对象(混合)(例如字符串和整数)插入到同一个列表中,这是一种反模式,会导致代码不可读(用您的泛型包试试吧!)
    • 因为你的编译器在你声明它之后知道你的 GenericBag 的类型,所以在你的代码的任何阶段,如果你将鼠标悬停在你的 genericBag 实例上,你就会知道它的类型,这使你的代码更具可读性,也可以对其他人进行扩展

    Generics 还提供更多方式,假设您希望您的 GenericBag 只接受数字,那么您可以这样写:

    public class GenericBag<T extends Number>
    

    我对你的建议是阅读一些关于 Java 基础知识,尤其是泛型的文章,拥有基于实践的学习方式是一件好事,但是有很多文章可以为你提供一些非常好的理论见解。

    https://www.baeldung.com/java-generics

    【讨论】:

      【解决方案2】:

      使用GenericBag&lt;String&gt; 而不是ObjectBag 的原因与使用String(或任何其他类型)而不是Object 基本相同:

      类型安全。

      您声明某个方法只返回一个字符串集合而不是其他任何东西,从而阻止您将其他对象放在那里,或者试图将您从包中得到的东西视为其他类型。如果您有 100 行代码,这听起来可能很愚蠢,但是当您使用体面的代码库时,这可能会为您节省大量调试时间。

      虽然,类型安全不是灵丹妙药,它只是一种工具,有些人觉得有用,有些人觉得没用。我很确定它是任何编程论坛的热门圣战话题。

      如果您觉得在没有这种范式的情况下工作很自在(Javascript 背景,对吗?),您可以考虑尝试一些动态类型语言,例如 Python,而不是 Java。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-02-13
        • 1970-01-01
        • 1970-01-01
        • 2012-03-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多