[Java]xxxble和xxxtor

之前看书以及看视频学习xxxble和xxxtor的接口时,我总觉得他们讲的很模糊搞得我很晕,最近整理了一下有了一些自己的想法。

xxxble

对于xxxble,可以理解为形容词,这个类是xxx的,Comparable这个类是可以比较的;Cloneable这个类是可以复制的;Iterable这个类是可以迭代的。

当我们在某个类中implements了这些xxxable的接口,这些接口通常会要求这个类重写一些方法。比如

public class MyTest implements Comparable {
    @Override
    public int compareTo(Object o) {
        return 0;
    }
}
public class MyTest implements Iterable {

    @Override
    public Iterator iterator() {
        return null;
    }
}

这些你可以理解为默认的方法。这里默认的意思就是,当我们在没有考虑其他xxxtor的情况下,调用某些方法时,我们可以直接把对象放进去就会执行这些默认的方法。比如不严谨地说,Collection.sort(new MyTest()),他就直接调用的是MyTest.compareTo()用来比较大小然后排序;for(var iter:new MyTest()),调用的就是MyTest.iterator()。

xxxble的好处是,如果要实现一些功能,你只需要传入对象即可,不需要额外的信息。

xxxtor

xxxtor,则更像描述这个类是某种工具

他的使用场景如下:有时候我们implements这些xxxble接口时,存在只提供一种默认方法是不够的情况;又或者说这个类根本就没有implements Comparable,但是你需要对他的对象进行比较(此时假设你没办法修改这个类,比如说JDK的String类你没办法直接修改)。比如:

public class MyTest implements Comparable {
    int age;
    int height;

    @Override
    public String toString() {
        return "MyTest{" +
                "age=" + age +
                ", height=" + height +
                '}';
    }

    public MyTest(int age, int height) {
        this.age = age;
        this.height = height;
    }

    @Override
    public int compareTo(Object o) {
        var tmp = (MyTest) o;
        return Integer.compare(age,tmp.age);
    }

    public static void main(String[] args) {
        List<MyTest> list = new ArrayList<MyTest>();
        list.add(new MyTest(2,20));
        list.add(new MyTest(8,19));
        list.add(new MyTest(6,60));

        Collections.sort(list);

        for(var iter:list){
            System.out.println(iter);
        }
    }
}

MyTest有两个属性,年龄和身高,有时候我们可能希望根据年龄比较,但某个时候我们又希望按身高比较。那么这时候我们仅仅说这个类是可比较的,Comparable就似乎不足了。

在可以修改原始类的情况下,你可以这样操作:

public class MyTest implements Comparable, Comparator {
    int age;
    int height;

    @Override
    public String toString() {
        return "MyTest{" +
                "age=" + age +
                ", height=" + height +
                '}';
    }

    public MyTest() {
    }

    public MyTest(int age, int height) {
        this.age = age;
        this.height = height;
    }

	//下面的Object都可以通过引入泛型,来解决
    /*default comparison based on age*/
    @Override
    public int compareTo(Object o) {
        var tmp = (MyTest) o;
        return Integer.compare(age, tmp.age);
    }

    @Override
    public int compare(Object o1, Object o2) {
        MyTest m1 = (MyTest)o1;
        MyTest m2 = (MyTest)o2;
        return Integer.compare(m1.height,m2.height);
        
    }

    public static void main(String[] args) {
        MyTest[] tests = {new MyTest(2, 20),
                new MyTest(8, 19),
                new MyTest(6, 60)};
		//因为MyTest()是Comparator,所以
		//Arrays.sort(tests)调用Comparable的方法
		//而这里则调用Comprator的方法
        Arrays.sort(tests, new MyTest());

        System.out.println(Arrays.toString(tests));
    }
}

这时候这个类就即使提供某种默认比较方法的类,同时又是某种自定义比较方法的类;当然你还可以使用nested class的修改方法,或者匿名类的方法。比如这样:

public class MyTest implements Comparable{
    int age;
    int height;

    @Override
    public String toString() {
        return "MyTest{" +
                "age=" + age +
                ", height=" + height +
                '}';
    }

    public MyTest(int age, int height) {
        this.age = age;
        this.height = height;
    }

    @Override
    public int compareTo(Object o) {
        var tmp = (MyTest) o;
        return Integer.compare(age,tmp.age);
    }


    public static Comparator<MyTest> compareHeight(){
        return new Comparator<MyTest>() {
        //anonymous class
            @Override
            public int compare(MyTest myTest, MyTest t1) {
                return Integer.compare(myTest.height,t1.height);
            }
        };
    }

    //------------------------------------
    public static void main(String[] args) {
        List<MyTest> list = new ArrayList<MyTest>();
        list.add(new MyTest(2,20));
        list.add(new MyTest(8,19));
        list.add(new MyTest(6,60));

        Collections.sort(list);

        for(var iter:list){
            System.out.println(iter);
        }
        System.out.println();

        Collections.sort(list, MyTest.compareHeight());

        for(var iter:list){
            System.out.println(iter);
        }

    }
    
}

同样的,对于Iterable和Iterator而言,如果这个类是可以迭代访问的,那么我们就说他是Iterable的,但如果这个类不是Iterable的,又或者你想使用非默认的迭代器,那么你就需要在这个类的内部定义一个内部类。

匿名类,lambda

但是,在当前这个例子中,实际上是有更好的方法的,比如说匿名类和lambda。

Arrays.sort(tests, new Comparator<MyTest>(){

    @Override
    public int compare(MyTest o1, MyTest o2) {
        MyTest m1 = (MyTest)o1;
        MyTest m2 = (MyTest)o2;
        return Integer.compare(m1.height,m2.height);
    }
});

此处不再让MyTest implements Comparator,而是直接传递一个Comparator匿名实现类。这样,MyTest就不需要既是可以比较的,然后还是一个比较器。或者更进一步,使用lambda

Arrays.sort(tests, (MyTest t1, MyTest t2) -> {
    return Integer.compare(t1.height, t2.height);
});

其他思考

我在《Data Structure & Algorithms in JAVA》这本书里经常看到类似于这样的代码

private class KeyIterator implements Iterator<K> {
    private Iterator<MyEntry<K, V>> entries = entrySet().iterator();

    @Override
    public boolean hasNext() {
        return entries.hasNext();
    }

    @Override
    public K next() {
        return entries.next().getKey();
    }

    public void remove() {
        throw new UnsupportedOperationException();
    }
}

private class KeyIterable implements Iterable<K> {

    @Override
    public Iterator<K> iterator() {
        return new KeyIterator();
    }
}

public Iterable<K> keySet() {
    return new KeyIterable();
}

这串代码是在一个AbstractMap类中定义的,首先是定义了一个iterator,这个iterator是Iterable这个函数接口的返回值。这两个类都被设计成private,并最后通过keySet暴露出来。

然后这一长串可以写成这个样子

public Iterable<K> keySet() {
    return () -> {
        return new Iterator<K>() {
            private Iterator<MyEntry<K, V>> entries = entrySet().iterator();

            public boolean hasNext() {
                return entries.hasNext();
            }

            public K next() {
                return entries.next().getKey();
            }

            public void remove() {
                throw new UnsupportedOperationException();
            }
        };
    };
}

写成这个样子是更紧凑,然后更方便理解,不然按原作者上面一直几个函数来回看挺烦人的。

按照上面xxxble是可以在某些函数中直接调用的理解,假设说我们这个不是Abstract类,那么这个类就可以直接for(var iter:类.keyset()){...}

这种应该是一种设计模式,因为书中对于Comparator 和 Comparable 也有类似的用法。这种模式我目前还不了解,但是他似乎很好的进行了一些分离,解耦合(?)。

总结

一个类可以是xxxble,此时将意味着他的对象,实例,本身就是xxxble的,如MyTest implements Comparable,则意味着他的对象就是可以直接拿来比较的(当做某种函数的参数直接使用,而无需其他额外的代码);一个类是xxxtor的,则意味着这个类是某种工具,他可以被用来做xxx相关的操作,如果这个类是函数接口,那么它的使用会极度简便。一个类可以既是xxxble,又是xxxtor,但我不确定这种设计是不是好的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值