• Java入门系列之重写


    前言

    关于所有Java系列文章面向有一定基础的童鞋,所写每一篇希望有一定含金量,有些内容可能会从Java整个语法全局考虑穿插后续要讲解的内容以成系统,若不理解,请看完后再学习。上一节我们讲解完了final关键字,本节我们继续来对比讲解Java和C#中的重写,二者语言的重写区分非常清晰,Java子类中基类方法签名一样或通过注解@Override显式声明,C#中基类通过virtual关键字修饰,子类通过ovveride关键字表示重写,具体细节请往下看。

    重写

    既然是重写必然就涉及到继承,我们首先来看看Java中的重写是怎样的呢?如下:

    public class Main {
        public void f() {
            System.out.println("Main.f");
        }
    
        public static void main(String[] args) {
            Main main = new Sub();
            main.f();
        }
    }
    
    class Sub extends Main {
        public void f() {
            System.out.println("Sub.f");
        }
    }

    当调用基类的f方法时,此时发现已被子类所重写,所以如上正常打印出Sub.f,要是我们将上述基类中方法修改为私有的呢

    可能我们期待输出子类中的打印结果,但是修改为私有后,说明此时对子类不再可见,也就相当于使用了final,在这种情况下,子类中方法则是一个全新的方法,很显然说明:只有非私有方法才可以被重写。对于这种情况下编译不会报错, 但是也不会按照我们所期望的结果来执行,所以建议对于基类中的私有方法命名为和子类不同的名字,为了让重写一目了然或更加明确,在1.5版本发布了注解功能,我们可以通过注解来显式声明要重写基类方法,若基类为私有,此时通过注解则会编译报错,因为找不到要重写的方法,这种体验更加友好,比如如下:

    public class Main {
        private void f() {
            System.out.println("Main.f");
        }
    
        public static void main(String[] args) {
            Main main = new Sub();
            main.f();
        }
    }
    
    class Sub extends Main {
    
        //编译错误,未找到基类(超类)中要重写的方法
        @Override
        public void f() {
            System.out.println("Sub.f");
        }
    }

    举一反三,我们来思考一个问题,是不是方法签名一致,子类就可以重写基类方法呢?很显然不是,若是静态方法,必然不能被重写,如果通过注解@Override声明那么必然编译报错,否则调用基类方法,对于接口中的静态方法同理。所以还是建议使用注解来声明重写。那么为什么通过注解显式声明重写基类方法或通过关键字final修饰方法就会在编译阶段报错呢?因为它们在编译阶段就完成了静态绑定,而不是运行时动态绑定。问题又来了,上述我们讲解到若在子类中不通过注解显式声明重写,同时在基类中方法私有,此时一定可以编译通过(上述已演示),并且会调用基类方法并打印出结果,事实情况一定是这样吗?很显然也不是如此,如下示例:

    class Super {
        private void f() {
            System.out.println("Super.f");
        }
    }
    
    class Derived extends Super {
        public void f() {
            System.out.println("Derived.f");
        }
        public static void main(String[] args) {
            Super aSuper = new Derived();
            
            //编译报错(因为基类方法私有)
            aSuper.f();
        }
    }

    初一看,这不是一样么,其实是不一样,上述可以那是因为调用方在基类里面,当然可以调用内部的私有方法,如上情况只对基类内部私有, 当然也就不能调用,这里就又引出一个问题,是不是声明为基类的私有方法,子类就无法进行重写呢?根据我们上述打印的结果来看,理论上不可行,事实情况是可以的,通过内部类(后续会出文章详细讲解)来实现。

    class Main {
    
        private void f() {
            System.out.println("Main.f");
        }
    
        class Inner extends Main {
            private void f() {
                System.out.println("Inner.f");
            }
        }
    
        public static void main(String args[]) {
    
            //内部类实例必须通过外部类实例创建
            Main outer = new Main();
            Inner inner = outer.new Inner();
    
            //内部类可以在内部访问外部的所有成员(包括私有)
            inner.f();
    
            // 调用外部类方法
            outer = inner;
            outer.f();
        }
    }

    C#若明确需要重写,那么基类方法声明为虚有的virtual,子类通过ovverride关键字修饰方法达到重写目的,若没有这两个关键字,和Java中一样只是方法签名一致,那么说明编译器会提醒是否通过new关键字来表明隐藏基类的方法

    class Program
    {
        public void F()
        {
            Console.WriteLine("Main.f");
        }
    
        static void Main(string[] args)
        {
            Program program = new Sub();
            program.F();
    
            Console.ReadKey();
        }
    }
    
    class Sub : Program
    {
        public new void F()
        {
            Console.WriteLine("Sub.f");
        }
    }

    总结

    Java和C#中的重写区分度非常清晰,Java中只要方法签名一致就可以达到重写,不过建议通过注解方法来显式声明重写以免引起不必要的问题,同时,即使基类方法私有,我们也可借助于内部类来实现重写。

  • 相关阅读:
    新线程 handler
    解决获取View的width和Height为0的4种方法
    回调深入理解 同步回调 以android中View.OnClickListener为列
    回调函数
    android:layout_weight
    studio rendering problems
    android:exported属性
    Codeforces 1264C/1265E Beautiful Mirrors with queries (概率期望、DP)
    Codeforces 1254C/1255F Point Ordering (交互题)
    Codeforces 576D Flights for Regular Customers (图论、矩阵乘法、Bitset)
  • 原文地址:https://www.cnblogs.com/CreateMyself/p/13276324.html
Copyright © 2020-2023  润新知