Java中向下转型的意义

2023-05-16

什么是向上转型和向下转型

在Java继承体系中,认为基类(父类/超类)在上层,导出类(子类/继承类/派生类)在下层,因此向上转型的意思就是把子类对象转成父类类型,即将父类的引用指向子类对象;向下转型的意思就是把父类对象转成子类类型,即将子类的引用指向父类对象。


向上转型

先看一下向上转型的例子:

首先定义一个动物类,即Animal类,并赋予一个eat()方法

class Animal  
 {  
    public void eat()  
    {  
        System.out.println("父类的 eating...");  
    }  
}  

然后定义一个鸟类,即Bird类,继承Animal类并重写Animal类的eat()方法,并赋予一个方法fly()方法

class Bird extends Animal  
{     
    @Override  
    public void eat()  
    {  
        System.out.println("子类重写的父类的  eatting...");  
    }     
    public void fly()  
    {  
        System.out.println("子类新方法  flying...");  
    }  
}  

最后定义一个Sys类,实现向上转型,先看代码

public class Sys  
{  
    public static void main(String[] args)   
    {  
        Animal b=new Bird(); //向上转型  
        b.eat();   
        //  b.fly(); b虽指向子类对象,但此时子类作为向上的代价丢失和父类不同的fly()方法  
        sleep(new Male());  
        sleep(new Female());//传入的参数是子类!!
    }  

    public static void sleep(Human h) //方法的参数是父类!!
        {  
             h.sleep();  
        }  
} 

好,看完代码了,我们来解释一下向上转型是如何进行的吧!

  • 向上转型的实现
Animal b=new Bird(); //向上转型
            b.eat(); // 调用的是子类的eat()方法
            b.fly(); // 报错!!!!!-------b虽指向子类对象,但此时子类作为向上转型的代价丢失和父类不同的fly()方法(子类向上转型会丢失与父类不同(父类所不拥有的方法)
  • 为何不直接Bird b=new Bird();b.eat() 呢?
    这样就没有体现出面向对象的抽象的编程思想,且降低了代码的可扩展性

  • 向上转型的好处?

 sleep(new Male());//调用方法时传入的参数是子类
           sleep(new Female());
             public static void sleep(Human h) //方法的参数是父类
            {
                 h.sleep();
             }

如上代码就是用的向上转型,若是不用向上转型,那么有多少个子类就得在这儿写多少种不同的睡觉方法,如果有一千个,甚至更多子类,还不写的奔溃,所以使用向上转型既提高了代码的可扩展性,又减少了程序员的工作,何乐而不为呢?


再来看一下向下转型:

向下转型

和上面一样,我们先定义一个水果类,即Fruit类,并赋予一个myName()方法

class Fruit{
    public void myName(){
        System.out.printf("superclass:Fruit");
    }
}

接着我们定义一个苹果类,即Apple类,继承Fruit类并重写Fruit类的myName()方法,并赋予一个myMore()方法

class Apple extends Fruit{
    public void myName(){
        System.out.printf("sonclass:Apple");
    }

    public void myMore(){
        System.out.printf("A little Apple of Fruit");
    }
}

最后定义一个Sys类来实现向下转型,但实现向下转型前需要先实现向上转型,所以这也是为什么我们前面要介绍向上转型的原因,先看代码

public class Sys{
    public static void main(String[] args) {   
        Fruit a=new Apple(); //向上转型  
        a.myName();  

        Apple aa=(Apple)a; //向下转型,编译和运行皆不会出错(正确的)  
        aa.myName();//向下转型时调用的是子类的myName()方法  
        aa.myMore();;  

        Fruit f=new Fruit();  
        Apple aaa=(Apple)f; //-不安全的---向下转型,编译无错但会运行会出错  
        aaa.myName();  
        aaa.myMore();   
    }  
}
  • 子类是父类的超集,子类拥有的信息(例如方法),父类不一定有。用子类类型去操作父类对象是不安全的。

  • 父类是子类的子集,父类拥有的公有信息(例如方法),子类一定有,所以用父类类型去操作子类对象是安全的,也即向上转型是成立的。

这里有必要再说一下向上转型:

向上转型可分为两种,一种是隐式转换,一种是强制转换。

class A {  

}  
interface C {  

}  
public class B extends A implements C {  

    public static void main(String[] args) throws Exception {  
        // B t = (B) new A(); // 向下转型不成立,运行时会抛出错误  

        /** 
         * 向上转型 
         */  
        // 隐式转换  
        A a1 = new B();  

        // 强制转换  
        A a2 = new B();  
        C c = (C) a2;  

        // 强制转换  
        Class<> clazz = Class.forName("p.B");  
        A a3 = (A) clazz.newInstance();  
    }  
}  

当子类有两种或多种父类类型,并且这些父类类型并不在同一个继承体系内时,如果要将子类对象的引用从一个父类传给另一个父类,就需要进行强制转换。

向下转型是你向上转型后才能执行的操作,一定要牢记这一点。

类型还原

  public class B {  

    /** 
     * @param args 
     * @throws Exception 
     */  
    public static void main(String[] args) throws Exception {  
        Object o = new B();// 向上转型  
        B b = (B) o;// 类型还原  

        b = (B) Class.forName("p.B").newInstance();  
    }  

}  

向下转型的实例

首先,我们定义一个空接口Food,不赋予任何方法

public interface Food{

}

然后新建一个Vegetables,并实现Food接口:

public class Vegetables implements Food{

    public void green(){
        System.out.println("Vegetables is green food");
    }

    public void nutrition(){
        System.out.println("Vegetables is very nourishing");
    }
}

新建一个Meal,并实现Food接口:

public class Meal implements Food{

    public void testgood(){
        System.out.println("Meal test good");
    }

    public void nutrition(){
        System.out.println("Meal is also nourishing");
    }
}

新建一个Fruit类,并实现Food接口:

public class Fruit implements Food{

    public void necessary(){
        System.out.println("Fruit is necessary food for man");
    }

    public void nutrition(){
        System.out.println("Meal is also nourishing");
    }
}

上面的程序都很简单,定义了一个Food接口,并在Vegetables、Meal、Fruit类分别实现它,每一个方法也很简单,仅仅打印了一行信息而已。

接下来,我们想象一个情景:我们在商城买食品,食品有很多,但基本上可以分为蔬菜、肉类和水果,等等,这些都属于食品。电子产品是抽象的。好,那么我们决定买一台Thinkpad,一个鼠标和一个键盘。
这时,我们需要一个购物车来放我们需要买的食品。我们可以添加进购物车,然后通过购物车还能知道存放的食品数量,能拿到对应的食品。

那么,一个购物车类就出来了,如下:

import java.util.ArrayList;
import java.util.List;

public class ShopCar{

    private List<Food> mlist = new ArrayList<Food>();

    public void add(Food food){

        mlist.add(food);
    }

    public int getSize(){

        return mlist.size();
    }

    public Electronics getListItem(int position){

        return mlist.get(position);
    }

}

List 集合是用来存放食品的,add 方法用来添加食品到购物车,getSize 方法用来获取存放的食品数量,getListItem 方法用来获取相应的食品。

大家可能会疑惑的是为什么是放 Electronics 的泛型,而不是放Vegetables,Meal,Fruit等?

想象一下,如果是List,肯定放不进肉Meal吧,难道我们要生成3个集合?这里是定义了3个食品类,但是我如果有100种食品呢,难道要定义100个集合?

这太可怕了。所以之前,我们写了一个Food接口,提供了一个Food的标准,然后让每一个Food子类都去实现这个接口。

实际上这里又涉及到了向上转型的知识点,我们虽然在add 方法将子类实例传了进来存放,但子类实例在传进去的过程中也进行了向上转型。

所以,此时购物车里存放的子类实例对象,由于向上转型成Food,已经丢失了子类独有的方法,以上述例子来分析,Vegetables实例就是丢失了green() 和nutrition() 这两个方法,而Meal实例就是丢失了testgood()和nutrition()这两个方法。

但是实际上,这些食物丢失任何一个方法都是我们所不希望的。

接着我们写一个测试类 Test 去测试购物车里的食品。

public class Test{

    public static final int Vegetables= 0;
    public static final int Meal= 1;
    public static final int Fruit = 2;

    public static void main(String[] args){

        //添加进购物车
        ShopCar shopcar = new ShopCar();
        shopcar.add(new Vegetables());
        shopcar.add(new Meal());
        shopcar.add(new Fruit ());

        //获取大小
        System.out.println("购物车存放的食品数量为 ——> "+shopcar.getSize());


        //开始测试Vegetables蔬菜
        Vegetables vegetables= (Vegetables)shopcar.getListItem(Vegetables);
        vegetables.green();
        vegetables.nutrition();

        System.out.println("-------------------");

        //开始测试Meal肉
        Meal meal = (Meal)shopcar.getListItem(Meal);
        meal .testgood();
        meal .nutrition();

        System.out.println("-------------------");

        //开始测试Fruit水果
        Fruit fruit = (Fruit )shopcar.getListItem(Fruit);
        fruit .necessary();
        fruit .nutrition();
    }

}

举个例子分析一下

//开始测试Vegetables蔬菜
        Vegetables vegetables= (Vegetables)shopcar.getListItem(Vegetables);
        vegetables.green();
        vegetables.nutrition();
(Vegetables)shopcar.getListItem(Vegetables);

这句代码是获取到Food类型的实例。不是Vegetables的实例

通过向下转型,赋值给子类引用

Vegetables vegetables= (Vegetables)shopcar.getListItem(Vegetables);

样子类实例又重新获得了因为向上转型而丢失的方法(gren和nutrition)


向下转型的意义

向下转型最大的作用是Java的泛型编程,作用巨大,Java中集合框架作用大都如此。

而在Android开发中,我们在Layout文件夹,用xml写的控件。为什么能在Activity等组件中通过 findViewById() 方法找到呢?为什么 findViewById(R.id.textview) 方法传入TextView的id后,还要转型为TextView呢?这就是 Java 向下转型的一个应用。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java中向下转型的意义 的相关文章

  • Java - 因内存不足错误而关闭

    关于如何最好地处理这个问题 我听到了非常矛盾的事情 并且陷入了以下困境 OOME 会导致一个线程崩溃 但不会导致整个应用程序崩溃 我需要关闭整个应用程序 但不能 因为线程没有剩余内存 我一直认为最佳实践是让它们离开 这样 JVM 就会死掉
  • NoInitialContextException:heroku 战争部署

    我一直在开发一个 J2EE 项目 并且在其中使用连接池 也通过部署在 heroku 上的数据库进行访问 我使用以下代码来设置 Connection 对象 Context initContext new InitialContext Cont
  • “java.io.IOException:连接超时”和“SocketTimeoutException:读取超时”之间有什么区别

    如果我设置一个套接字 SoTimeout 并从中读取 当读取时间超过超时限制时 我会收到 SocketTimeoutException 读取超时 这是我的例子中的堆栈 java net SocketTimeoutException Read
  • Android 中的列表(特别是 RecyclerView 和 CardView)如何工作

    请原谅我问这个问题 但我是 Android 开发新手 尽管我正在尝试了解developer android com 网站上的基础知识 但大多数示例 即使他们说它们是为 Android Studio 构建的 尚未设置为使用 Gradle 因此
  • 从 MS Access 中提取 OLE 对象(Word 文档)

    我有一个 Microsoft Access 数据库 其中包含一个包含 Microsoft Word 文档的 OLE 对象字段 我试图找到代码来检索保存在 OLE 对象中的文件 以便用户可以从我的 JavaFx 应用程序中的按钮下载它 但没有
  • FileNotFoundException - Struts2 文件上传

    Strange FileNotFoundException使用Struts2上传文件时 这是 JSP 的一部分
  • Java中的断点和逐步调试?

    抱歉我的问题名称很奇怪 我不知道如何寻找这个 因为我不知道这些东西是如何称呼的 Visual Studio 中至少有一个功能 您可以单击代码左侧并设置一个大红点的起点 然后运行程序 您可以通过按 f8 或 f5 实际上是不同的 f 来跟踪步
  • 在 MongoDB 和 Apache Solr 之间同步数据的简单方法

    我最近开始使用 MongoDB 和 Apache Solr 我使用 MongoDB 作为数据存储 并且希望 Apache Solr 为我的数据创建索引 以实现应用程序中的搜索功能 经过一些研究 我发现 基本上有两种方法可以在 MongoDB
  • 如何使用正则表达式验证 1-99 范围?

    我需要验证一些用户输入 以确保输入的数字在 1 99 范围内 含 这些必须是整数 Integer 值 允许前面加 0 但可选 有效值 1 01 10 99 09 无效值 0 007 100 10 5 010 到目前为止 我已经制定了以下正则
  • 通过 appassembler-maven-plugin 生成的脚本无法在 Spring Boot 应用程序中找到主类

    我使用 appassembler maven plugin 生成的启动脚本有问题 我有一个基本的 spring boot 应用程序 只有一个类 SpringBootApplication public class ScriptDemoApp
  • 如何停止执行的 Jar 文件

    这感觉像是一个愚蠢的问题 但我似乎无法弄清楚 当我在 Windows 上运行 jar 文件时 它不会出现在任务管理器进程中 我怎样才能终止它 我已经尝试过 TASKKILL 但它对我也不起作用 On Linux ps ef grep jav
  • 无法在 Java/Apache HttpClient 中处理带有垂直/管道栏的 url

    例如 如果我想处理这个网址 post new HttpPost http testurl com lists lprocess action LoadList 401814 1 Java Apache 不允许我这么做 因为它说竖线 是非法的
  • JAVA中遍历JSON数据

    我是 JSON 新手 我使用 HTTPUrlConnections 并在 JAVA 程序中获得一些响应 响应数据将类似于 data id 1 userId 1 name ABC modified 2014 12 04 created 201
  • IntelliJ 组织导入

    IntelliJ 是否具有类似于 Eclipse 中的组织导入功能 我拥有的是一个 Java 文件 其中多个类缺少导入 例子 package com test public class Foo public Map map public J
  • Lombok @Builder 不创建不可变对象?

    在很多网站上 我看到 lombok Builder 可以用来创建不可变的对象 https www baeldung com lombok builder singular https www baeldung com lombok buil
  • 如何从 Ant 启动聚合 jetty-server JAR?

    背景 免责声明 I have veryJava 经验很少 我们之前在 Ant 构建期间使用了 Jetty 6 的包装版本来处理按需静态内容 JS CSS 图像 HTML 因此我们可以使用 PhantomJS 针对 HTTP 托管环境运行单元
  • 源值 1.5 的错误已过时,将在未来版本中删除

    我使用 scala maven plugin 来编译包含 scala 和 java 代码的项目 我已经将源和目标设置为1 7 但不知道为什么maven仍然使用1 5 这是我在 pom xml 中的插件
  • Java 的 PriorityQueue 与最小堆有何不同?

    他们为什么命名PriorityQueue如果你不能插入优先级 它看起来与堆非常相似 有什么区别吗 如果没有区别那为什么叫它PriorityQueue而不是堆 默认的PriorityQueue是用Min Heap实现的 即栈顶元素是堆中最小的
  • 使用 JFreeChart 为两个系列设置不同的 y 轴

    我正在使用 JFreeChart 使用折线图绘制两个数据系列 XYSeries 复杂的因素是 其中一个数据系列的 y 值通常远高于第二个数据系列的 y 值 假设第一个系列的 y 值约为数百万数量级 而第二个数据系列的 y 值约为数百万数量级
  • 即使调整大小,如何获得屏幕的精确中间位置

    好的 这个问题有两部分 当我做一个JFrame 并在其上画一些东西 即使我将宽度设置为 400 并使其在一个项目击中它时 当然 允许项目宽度 它会反弹回来 但由于某种原因 它总是偏离屏幕约 10 个像素 有没有办法解决这个问题 或者我只需要

随机推荐

  • 电池、电机、螺旋桨搭配

    电池 电机 螺旋桨搭配 span class hljs number 1 span 电机 span class hljs number 1 span 电机KV值 xff1a 大KV配小桨 xff0c 小KV配大桨 KV值是每1V的电压下电机
  • 为什么指针变量做形参可以改变实参的数据

    形参不能传任何东西给实参 xff0c 实参传过去的东西都是一个副本 下面以一个交换数据的被调函数片段为例 在指针变量由实参传递给形参时传过去的实际是指针变量的值 xff0c 即一个地址 xff0c 在 t 61 p1 p1 61 p2 p2
  • 敲线性表代码时遇到的问题(C++)【exit,return】

    1 xff1a exit OVERFLOW exit简介 为C 43 43 的退出函数 xff0c 声明于stdlib h中 xff0c 对于C 43 43 其标准的头文件为cstdlib 声明为 void exit int value e
  • /etc目录详解

    Linux etc目录详解 etc目录 包含很多文件 许多网络配置文件也在 etc 中 etc rc or etc rc d or etc rc d 启动 或改变运行级时运行的scripts或scripts的目录 etc passwd 用户
  • Tomcat 8.0 Mac 安装与配置

    工具 原料 1 xff0c JDK xff1a 版本为jdk 8u40 macosx x64 dmg 下载地址http www oracle com technetwork java javase downloads jdk8 downlo
  • 2011届移动开发者大会

    2011年11月4号星期五 xff0c 早晨八点我们就早早的来到了会场 xff0c 因为有了上次云计算大会的经验 xff0c 所以我们早早的就来了 xff0c 因为人很多我们必须才能找到一个比较好的位置 由于来的太早工作人员很多都没有就位
  • 第五篇 openvslam建图与优化模块梳理

    建图模块 mapping module在初始化系统的时候进行实例化 xff0c 在构建实例的时候会实例化local map cleaner和local bundle adjuster 系统启动的时候会在另外一个线程中启动该模块 code s
  • 个人安卓学习笔记---java.io.IOException: Unable to open sync connection!

    在使用手机调试程序的时候出现了java io IOException Unable to open sync connection 这样的异常 xff0c 我尝试使用拔掉USB然后重新 xff0c 插入 xff0c 结果失败 再尝试 xff
  • "_OBJC_CLASS_$_Play", referenced from:

    IOS做了这么久也没写过什么博客 xff0c 不好不好 xff0c 今天开始写 遇到的问题 xff1a 34 OBJC CLASS Play 34 referenced from 解决方案 xff1a Tagert Build Phases
  • 树莓派SSH远程连接连接失败的解决办法

    树莓派SSH远程连接 将全新的树莓派系统烧录 xff0c 开机然后用SSH远程连接 xff0c 结果SSH连接提示 connection refused xff0c 导致连接树莓派失败 出现错误的原因是自 2016 11 25 官方发布的新
  • 在树莓派中安装ROS系统(Kinetic)

    在树莓派中安装ROS系统 重新梳理了一下树莓派的安装流程 xff0c 现在我们来开始吧 打开官网教程 http wiki ros org kinetic step1 安装源 xff08 中国 xff09 sudo sh c 39 etc l
  • ROS学习笔记-roscd指令

    对ROS文件系统而言 xff0c ROS中的roscd命令实现利用包的名字直接切换到相应的文件目录下 xff0c 命令使用方法如下 xff1a span class hljs tag roscd span span class hljs a
  • configure it with blueman-service

    用下面这个命令把Linux目录的名字由中文改成英文了 export LANG span class hljs subst 61 span en US xdg span class hljs attribute user span span
  • 关于Ubuntu16.04升级系统后启动报错问题的修复

    关于Ubuntu16 04升级系统后启动报错问题的修复 Ubuntu16 04升级后启动报错为 Failed to start Load Kernel Modules 使用systemctl status systemd modules l
  • Ubuntu Mate 自动登录

    树莓派安装Ubuntu Mate 设置自动启动 需要修改文件 usr share lightdm lightdm conf d 60 lightdm gtk greeter conf sudo vim usr share lightdm l
  • Linux系统调用实现简析

    1 前言 限于作者能力水平 xff0c 本文可能存在谬误 xff0c 因此而给读者带来的损失 xff0c 作者不做任何承诺 2 背景 本篇基于 Linux 4 14 43 ARM 32 43 glibc 2 31 进行分析 3 系统调用的实
  • Docker中容器的备份、恢复和迁移

    1 备份容器 首先 xff0c 为了备份 Docker中的容器 xff0c 我们会想看看我们想要备份的容器列表 要达成该目的 xff0c 我们需要在我们运行着 Docker 引擎 xff0c 并已创建了容器的 Linux 机器中运行 doc
  • 关于OpenCV的那些事——相机姿态更新

    上一节我们使用张正友相机标定法获得了相机内参 xff0c 这一节我们使用 PnP Perspective n Point 算法估计相机初始姿态并更新之 推荐3篇我学习的博客 xff1a 姿态估计 Pose estimation algori
  • Java中接口(Interface)的定义和使用

    有关 Java 中接口的使用相信程序员们都知道 xff0c 但是你们知不知道接口到底有什么用呢 xff1f 毫无疑问 xff0c 接口的重要性远比想象中重要 接下来我们便一起来学习Java中接口使用 Java接口是什么 Java接口是一系列
  • Java中向下转型的意义

    什么是向上转型和向下转型 在Java继承体系中 xff0c 认为基类 xff08 父类 超类 xff09 在上层 xff0c 导出类 xff08 子类 继承类 派生类 xff09 在下层 xff0c 因此向上转型的意思就是把子类对象转成父类