-
定义函数
-
PL/SQL定义的类中,函数的定义有两种方式;
- MEMBER型函数:该函数需要通过对象进行定义,使用MEMBER定义的函数可以利用SELF关键字访问类中的属性内容;
- STATIC型函数:该函数独立于类之外,可以直接通过类名称进行调用,使用STATIC定义的函数无法访问类中的属性;
- 说明:SELF与JAVA中的this关键字具有一样的效果;
-
-
对比STATIC与MEMBER函数定义的区别
- 示例1:使用两种不同的方式来定义函数
CREATE OR REPLACE TYPE emp_object AS OBJECT( atri_empno NUMBER(4) , -- 雇员编号 -- 修改当前雇员编号的工资,使用类中的empno和sal属性 MEMBER PROCEDURE change_emp_sal_proc(p_sal NUMBER) , -- 取得当前雇员的工资 MEMBER FUNCTION get_emp_sal_fun RETURN NUMBER , -- 修改指定部门的全体雇员工资 STATIC PROCEDURE change_dept_sal_proc(p_deptno NUMBER , p_sal NUMBER) , -- 取得此部门的工资总和 STATIC FUNCTION get_dept_sal_sum_fun(p_deptno NUMBER) RETURN NUMBER ) NOT FINAL ; / |
本程序分别定义了两组子程序,一组采用MEMBER定义,另外一组采用STATIC进行定义; |
- 接上例:定义类体实现类规范
CREATE OR REPLACE TYPE BODY emp_object AS MEMBER PROCEDURE change_emp_sal_proc(p_sal NUMBER) AS BEGIN -- 使用SELF.atri_empno找到本类中的属性,即:更新当前对象中雇员工资 UPDATE emp SET sal=p_sal WHERE empno=SELF.atri_empno ; END ; MEMBER FUNCTION get_emp_sal_fun RETURN NUMBER AS v_sal emp.sal%TYPE ; v_comm emp.comm%TYPE ; BEGIN -- 取得当前对象中指定雇员编号的工资 SELECT sal,NVL(comm,0) INTO v_sal,v_comm FROM emp WHERE empno=SELF.atri_empno ; RETURN v_sal + v_comm ; END ; STATIC PROCEDURE change_dept_sal_proc(p_deptno NUMBER , p_sal NUMBER) AS BEGIN -- 更新指定部门全部雇员的工资 UPDATE emp SET sal=p_sal WHERE deptno=p_deptno ; END ; STATIC FUNCTION get_dept_sal_sum_fun(p_deptno NUMBER) RETURN NUMBER AS v_sum NUMBER ; BEGIN -- 查询指定部门的工资总和 SELECT SUM(sal) INTO v_sum FROM emp WHERE deptno=p_deptno ; RETURN v_sum ; END ; END ; / |
此时类体中已经实现了类规范所定义的子程序,可以发现在MEMBER函数中可以利用"SELF.属性"的方式取得本对象中的属性内容(保存在类规范之中),而在STATIC函数中无法使用属性 |
-
接上例:编写PL/SQL块实例化类对象
DECLARE v_emp emp_object ; BEGIN v_emp := emp_object(7369) ; -- 实例化emp_object类对象 v_emp.change_emp_sal_proc(3800) ; -- 修改7369工资 DBMS_OUTPUT.put_line('7369雇员工资:' || v_emp.get_emp_sal_fun()) ; DBMS_OUTPUT.put_line('10部门工资总和:' || emp_object.get_dept_sal_sum_fun(10)) ;-- 通过类调用 emp_object.change_dept_sal_proc(10,7000) ; -- 通过类调用 END ; / |
此时的程序分别采用了对象调用MEMBER函数并通过类调用STATIC函数的方式进行了类的功能操作。通过此时的操作可以发现,STATIC函数完全独立类之外,并且无法通过对象进行调用 |
-
构造函数
- 说明:当创建一个类的对象时,如果希望可以自动完成某些操作,例如为对象中的属性赋值,可以利用构造函数;
-
定义要求:
- 构造函数的名称必须与类名称保持一致;
- 构造函数必须使用CONSTRUCTOR关键字进行定义;
- 构造函数必须定义返回值,且返回值类型必须为SELF AS RESULT;
- 构造函数也可以进行重载,重载的构造函数参数的类型及个数不同;
- 默认的构造函数需要传入全部属性的值;
-
示例1:定义类规范
CREATE OR REPLACE TYPE emp_object AS OBJECT( atri_empno NUMBER(4) , -- 雇员编号 atri_sal NUMBER(7,2) , -- 雇员工资 atri_comm NUMBER(7,2) , -- 雇员佣金 -- 定义构造函数,只接收雇员编号 CONSTRUCTOR FUNCTION emp_object(p_empno NUMBER) RETURN SELF AS RESULT , -- 重载构造函数,接收雇员编号及佣金 CONSTRUCTOR FUNCTION emp_object(p_empno NUMBER , p_comm NUMBER) RETURN SELF AS RESULT ) NOT FINAL ; / |
本规范使用了CONSTRUCTOR关键定义了一个emp_object类的构造函数,在此构造函数中将只接受雇员编号一个参数; |
-
接上例:定义类体,实现类规范
CREATE OR REPLACE TYPE BODY emp_object AS CONSTRUCTOR FUNCTION emp_object (p_empno NUMBER) RETURN SELF AS RESULT AS BEGIN SELF.atri_empno := p_empno ; -- 保存雇员编号属性 -- 查询指定雇员的工资,并将其内容赋值给atri_sal属性 SELECT sal INTO SELF.atri_sal FROM emp WHERE empno=p_empno ; RETURN ; END ; CONSTRUCTOR FUNCTION emp_object(p_empno NUMBER , p_comm NUMBER) RETURN SELF AS RESULT AS BEGIN SELF.atri_empno := p_empno ; SELF.atri_comm := p_comm ; SELF.atri_sal := 200.0 ; -- 为atri_sal设置默认值 RETURN ; END ; END ; / |
-
接上例:使用PL/SQL块测试构造函数
DECLARE v_emp1 emp_object ; v_emp2 emp_object ; v_emp3 emp_object ; BEGIN v_emp1 := emp_object(7369,3500) ; -- 自定义构造函数 v_emp2 := emp_object(7566) ; -- 自定义构造函数 v_emp3 := emp_object(7839,0.0) ; -- 默认构造函数 DBMS_OUTPUT.put_line('7369雇员工资:' || v_emp1.atri_sal) ; DBMS_OUTPUT.put_line('7566雇员工资:' || v_emp2.atri_sal) ; DBMS_OUTPUT.put_line('7839雇员工资:' || v_emp3.atri_sal) ; END ; / |
此程序使用了3个不同的构造函数进行对象的创建,其中一个为默认生成的构造函数(要接收全部属性内容),而另外两个为用户自定义的构造函数(使用CONSTRUCTOR定义),在用户自定义的构造函数中,会根据传入的雇员编号查找此雇员的工资,并将查询出来的结果赋值给atri_sal属性。而另外一个构造函数会使用一个默认的数值为atri_sal属性赋值 |
-
定义MAP与ORDER函数
- 说明:当用户声明了多个类对象后,如果要对这些对象的信息进行排序,就不能按照NUMBER或VARCHAR2这种基本数据类型的方式进行排序了,必须专门指定比较的规则;
-
设置比较规则通过两个函数完成:
- MAP函数:使用MAP定义的函数将会按照用户定义的数据组合值区大小,然后利用ORDER BY子句进行排序;
- ORDER函数:ORDER函数与MAP函数类似,也是定义了一个排序规则,在进行数据排序时会默认调用。同时ORDER函数还可以比较两个对象的大小关系,所以如果要比较多个对象时ORDER函数会被重复调用,性能不如MAP函数;
- 提示:当用户定义一个类时,这两种函数只能2选1;
-
示例1:在类规范中定义MAP函数
CREATE OR REPLACE TYPE emp_object_map AS OBJECT( atri_empno NUMBER(4) , -- 雇员编号 atri_ename VARCHAR2(10) , -- 雇员姓名 atri_sal NUMBER(7,2) , -- 雇员工资 atri_comm NUMBER(7,2) , -- 雇员佣金 -- 定义MAP函数,此函数会在进行排序时自动调用 MAP MEMBER FUNCTION compare RETURN NUMBER ) NOT FINAL ; / |
本类中使用MAP关键字定义了一个compare()函数,同时此函数返回一个数字,,该数字为用户自定义的一个排序规则组合 |
-
接上例: 定义类体实现MAP函数
CREATE OR REPLACE TYPE BODY emp_object_map AS MAP MEMBER FUNCTION compare RETURN NUMBER AS BEGIN RETURN SELF.atri_sal + SELF.atri_comm ; END ; END ; / |
在类体中实现了compare()函数,同时在此函数中返回的是"工资+佣金"的数据组合,这样在使用ORDER BY排序时,将采用此函数的返回值进行大小排序 |
-
接上例:编写测试用数据库创建脚本
-- 按照emp_object_map的结构创建一张新的数据表,这样就可以使用MAP函数进行排序 CREATE TABLE emp_object_map_tab OF emp_object_map ; INSERT INTO emp_object_map_tab(atri_empno,atri_ename,atri_sal,atri_comm) VALUES (7369,'SMITH',800,0) ; INSERT INTO emp_object_map_tab(atri_empno,atri_ename,atri_sal,atri_comm) VALUES (7902,'FORD',3000,0) ; INSERT INTO emp_object_map_tab(atri_empno,atri_ename,atri_sal,atri_comm) VALUES (7499,'ALLEN',1600,300) ; INSERT INTO emp_object_map_tab(atri_empno,atri_ename,atri_sal,atri_comm) VALUES (7521,'WARD',1250,500) ; INSERT INTO emp_object_map_tab(atri_empno,atri_ename,atri_sal,atri_comm) VALUES (7839,'KING',5000,0) ; COMMIT ; |
- 接上例:通过查询实现排序
SELECT VALUE(e) ve , e.atri_empno , e.atri_ename , e.atri_sal+e.atri_comm FROM emp_object_map_tab e ORDER BY ve ; |
在本程序中,使用了一个VALUE()函数将emp_object_map_tab表中的数据取出,当利用ORDER BY 进行排序时,就会自动调用emp_object_map类中的MAP函数(compare)实现数据的组合后进行排序 |
使用MAP函数可以实现一组数据组合排序,而使用ORDER函数可以实现两个对象间的排序 |
-
示例2:定义类规范使用ORDER定义函数
CREATE OR REPLACE TYPE emp_object_order AS OBJECT( atri_empno NUMBER(4) , -- 雇员编号 atri_ename VARCHAR2(10) , -- 雇员姓名 atri_sal NUMBER(7,2) , -- 雇员工资 atri_comm NUMBER(7,2) , -- 雇员佣金 -- 定义ORDER函数,此函数可以用于两个对象间的比较 ORDER MEMBER FUNCTION compare(obj emp_object_order) RETURN NUMBER ) NOT FINAL ; / |
本程序将之前的MAP声明修改为ORDER声明,同时在compare()函数中传递了一个emp_object_order的对象,这样就可以用当前对象的数据与传入的对象数据进行比较 |
-
接上例:定义类体实现类规范,同时实现ORDER类型函数。
CREATE OR REPLACE TYPE BODY emp_object_order AS ORDER MEMBER FUNCTION compare(obj emp_object_order) RETURN NUMBER AS BEGIN IF (SELF.atri_sal + SELF.atri_comm) > (obj.atri_sal + obj.atri_comm) THEN RETURN 1 ; ELSIF (SELF.atri_sal + SELF.atri_comm) < (obj.atri_sal + obj.atri_comm) THEN RETURN -1 ; ELSE RETURN 0 ; END IF ; END ; END ; / |
- 接上例:定义PL/SQL块进行对象排序
DECLARE v_emp1 emp_object_order ; v_emp2 emp_object_order ; BEGIN v_emp1 := emp_object_order(7499,'ALLEN',1600,300) ; v_emp2 := emp_object_order(7521,'WARD',1250,500) ; IF v_emp1 > v_emp2 THEN DBMS_OUTPUT.put_line('7499的工资高于7521的工资。') ; ELSIF v_emp1 < v_emp2 THEN DBMS_OUTPUT.put_line('7499的工资低于7521的工资。') ; ELSE DBMS_OUTPUT.put_line('7499的工资与7521的工资相同。') ; END IF ; END ; / |
-
接上例:除了在PL/SQL中进行比较之外,也可以利用此类型创建数据表通过ORDER BY 进行排序
- 创建数据表
-- 根据emp_object_order创建数据表 CREATE TABLE emp_object_order_tab OF emp_object_order ; INSERT INTO emp_object_order_tab(atri_empno,atri_ename,atri_sal,atri_comm) VALUES (7369,'SMITH',800,0) ; INSERT INTO emp_object_order_tab(atri_empno,atri_ename,atri_sal,atri_comm) VALUES (7902,'FORD',3000,0) ; INSERT INTO emp_object_order_tab(atri_empno,atri_ename,atri_sal,atri_comm) VALUES (7499,'ALLEN',1600,300) ; INSERT INTO emp_object_order_tab(atri_empno,atri_ename,atri_sal,atri_comm) VALUES (7521,'WARD',1250,500) ; INSERT INTO emp_object_order_tab(atri_empno,atri_ename,atri_sal,atri_comm) VALUES (7839,'KING',5000,0) ; COMMIT ; |
- 进行数据查询,同时使用ORDER BY 排序
SELECT VALUE(e) ve , e.atri_empno , e.atri_ename , e.atri_sal+e.atri_comm FROM emp_object_order_tab e ORDER BY ve ; |
-
对象嵌套关系
- 说明:利用PL/SQL的面向对象编程除了可以将基本数据类型定义为属性之外,还可以结合对象的引用传递方式,进行对象类型的嵌套;
-
示例1:定义类规范
-- 删除emp_object DROP TYPE emp_object ; -- 定义部门类 CREATE OR REPLACE TYPE dept_object AS OBJECT ( atri_deptno NUMBER(2) , -- 部门编号 atri_dname VARCHAR2(14) , -- 部门名称 atri_loc VARCHAR2(13) , -- 部门位置 -- 取得对象信息 MEMBER FUNCTION tostring RETURN VARCHAR2 ) NOT FINAL ; / -- 定义雇员类,每一个雇员属于一个部门,所以设置了一个atri_dept的属性 CREATE OR REPLACE TYPE emp_object AS OBJECT( atri_empno NUMBER(4) , -- 雇员编号 atri_ename VARCHAR2(10) , -- 雇员姓名 atri_job VARCHAR2(9) , -- 雇员职位 atri_hiredate DATE , -- 雇佣日期 atri_sal NUMBER(7,2) , -- 雇员工资 atri_comm NUMBER(7,2) , -- 雇员佣金 atri_dept dept_object , -- 雇员部门 -- 取得对象信息 MEMBER FUNCTION tostring RETURN VARCHAR2 ) NOT FINAL ; / |
本程序定义了两个类规范(dept_object,emp_object),由于每一位雇员都有一个自己的部门信息,所以在emp_object类中设置了一个dept_object类型的属性,同时本程序在两个类规范中都定义了tostring()函数,此函数将返回对象的基本信息 |
-
接上例:定义类体实现类规范
-- 定义dept_object类体 CREATE OR REPLACE TYPE BODY dept_object AS MEMBER FUNCTION tostring RETURN VARCHAR2 AS BEGIN RETURN '部门编号:' || SELF.atri_deptno || ',名称:' || SELF.atri_dname || ',位置:' || SELF.atri_loc ; END ; END ; / -- 定义emp_object类体 CREATE OR REPLACE TYPE BODY emp_object AS MEMBER FUNCTION tostring RETURN VARCHAR2 AS BEGIN RETURN '雇员编号:' || SELF.atri_empno || ',姓名:' || SELF.atri_ename || ',职位:' || SELF.atri_job || ',雇佣日期:' || TO_CHAR(SELF.atri_hiredate,'yyyy-mm-dd') || ',工资:' || SELF.atri_sal || ',佣金:' || SELF.atri_comm ; END ; END ; / |
- 接上例:编写PL/SQL块验证关系
DECLARE v_dept dept_object ; v_emp emp_object ; BEGIN -- 首先定义部门对象,此对象需要通过emp_object类的构造方法保存到v_emp对象属性之中 v_dept := dept_object(10,'ACCOUNTING','NEW YORK') ; -- 定义雇员对象,传递此雇员所属的部门对象 v_emp := emp_object(7839,'KING','PRESIDENT',TO_DATE('1981-11-11','yyyy-mm-dd'),5000,null,v_dept) ; -- 直接输出雇员的完整信息 DBMS_OUTPUT.put_line(v_emp.tostring()) ; -- 根据信息找到其对应的部门信息 DBMS_OUTPUT.put_line(v_emp.atri_dept.tostring()) ; END ; / |
-
继承性
- 说明:在PL/SQL中实现集成,可以利用UNDER关键实现
-
示例1:定义父类 —— person_object
-- 定义Person类规范 CREATE OR REPLACE TYPE person_object AS OBJECT ( atri_pid NUMBER , -- 人员编号 atri_name VARCHAR2(10) , -- 人员姓名 atri_sex VARCHAR2(10) , -- 人员性别 MEMBER FUNCTION get_person_info_fun RETURN VARCHAR2 ) NOT FINAL ; / -- 定义Person类体 CREATE OR REPLACE TYPE BODY person_object AS MEMBER FUNCTION get_person_info_fun RETURN VARCHAR2 AS BEGIN RETURN '人员编号:' || SELF.atri_pid || ',姓名:' || SELF.atri_name || ',性别:' || SELF.atri_sex ; END ; END ; / |
-
接上例:定义子类 —— emp_object
-- 定义Emp类规范,此类为Person子类 CREATE OR REPLACE TYPE emp_object UNDER person_object ( atri_job VARCHAR2(9) , -- 雇员职位 atri_sal NUMBER(7,2) , -- 雇员工资 atri_comm NUMBER(7,2) , -- 雇员佣金 MEMBER FUNCTION get_emp_info_fun RETURN VARCHAR2 ) ; / -- 定义Emp类体 CREATE OR REPLACE TYPE BODY emp_object AS MEMBER FUNCTION get_emp_info_fun RETURN VARCHAR2 AS BEGIN RETURN '人员编号:' || SELF.atri_pid || ',姓名:' || SELF.atri_name || ',性别:' || SELF.atri_sex || ',职位:' || SELF.atri_job || ',工资:' || SELF.atri_sal || ',佣金:' || SELF.atri_comm ; END ; END ; / |
-
接上例:利用PL/SQL程序块测试
DECLARE v_emp emp_object ; BEGIN -- 此处必须明确写出父类与子类的全部参数 -- person_object类需要传入三个参数:人员编号、姓名、性别 -- emp_object类需要传入三个参数:职位、工资、佣金 v_emp := emp_object(7369,'SMITH','FEMALE','CLERK',800.0,0.0) ; DBMS_OUTPUT.put_line('person_object类的函数:' || v_emp.get_person_info_fun()) ; DBMS_OUTPUT.put_line('emp_object类的函数:' || v_emp.get_emp_info_fun()) ; END ; / | 程序运行结果: person_object类的函数:人员编号:7369,姓名:SMITH,性别:FEMALE emp_object类的函数:人员编号:7369,姓名:SMITH,性别:FEMALE,职位:CLERK,工资:800,佣金:0 |
-
函数覆写
- 说明:子类要覆写父类的函数,必须在定义时,使用OVERRIDING关键字,定义某一个函数为覆写函数;
-
示例1:定义程序,实现函数的覆写
-- 定义Person类规范 CREATE OR REPLACE TYPE person_object AS OBJECT ( atri_pid NUMBER , -- 人员编号 atri_name VARCHAR2(10) , -- 人员姓名 atri_sex VARCHAR2(10) , -- 人员性别 MEMBER FUNCTION tostring RETURN VARCHAR2 ) NOT FINAL ; / -- 定义Person类体 CREATE OR REPLACE TYPE BODY person_object AS MEMBER FUNCTION tostring RETURN VARCHAR2 AS BEGIN RETURN '人员编号:' || SELF.atri_pid || ',姓名:' || SELF.atri_name || ',性别:' || SELF.atri_sex ; END ; END ; / -- 定义Emp类规范,此类为Person子类 CREATE OR REPLACE TYPE emp_object UNDER person_object ( atri_job VARCHAR2(9) , -- 雇员职位 atri_sal NUMBER(7,2) , -- 雇员工资 atri_comm NUMBER(7,2) , -- 雇员佣金 -- 此函数名称与父类函数名称一样,所以此处为函数的覆写 OVERRIDING MEMBER FUNCTION tostring RETURN VARCHAR2 ) ; / -- 定义Emp类体 CREATE OR REPLACE TYPE BODY emp_object AS OVERRIDING MEMBER FUNCTION tostring RETURN VARCHAR2 AS BEGIN RETURN '人员编号:' || SELF.atri_pid || ',姓名:' || SELF.atri_name || ',性别:' || SELF.atri_sex || '职位:' || SELF.atri_job || ',工资:' || SELF.atri_sal || ',佣金:' || SELF.atri_comm ; END ; END ; / |
此时在emp_object类中明确使用了OVERRIDING覆写了person_object类中的tostring()函数,这样emp_object类体中就可以继续使用此函数名称 |
-
接上例:编写PL/SQL块测试程序
DECLARE v_emp emp_object ; BEGIN -- 此处必须明确写出父类与子类的全部参数 -- person_object类需要传入三个参数:人员编号、姓名、性别 -- emp_object类需要传入三个参数:职位、工资、佣金 v_emp := emp_object(7369,'SMITH','FEMALE','CLERK',800.0,0.0) ; DBMS_OUTPUT.put_line(v_emp.tostring()) ; END ; / |
-
对象多态性
-
多态体现在以下两个方面:
- 函数的多态性:体现为函数的重载与覆写;
- 对象的多态性:子类对象可以为父类对象进行实例化;
-
示例1:此示例继续沿用笔记七的例子中的person_object和emp_object类,同时为person_object类增加一个student_object子类;
-
在原有程序基础上增加新的子类
-
-
-- 定义Person类规范 CREATE OR REPLACE TYPE person_object AS OBJECT ( atri_pid NUMBER , -- 人员编号 atri_name VARCHAR2(10) , -- 人员姓名 atri_sex VARCHAR2(10) , -- 人员性别 MEMBER FUNCTION tostring RETURN VARCHAR2 ) NOT FINAL ; / -- 定义Person类体 CREATE OR REPLACE TYPE BODY person_object AS MEMBER FUNCTION tostring RETURN VARCHAR2 AS BEGIN RETURN '人员编号:' || SELF.atri_pid || ',姓名:' || SELF.atri_name || ',性别:' || SELF.atri_sex ; END ; END ; / -- 定义Emp类规范,此类为Person子类 CREATE OR REPLACE TYPE emp_object UNDER person_object ( atri_job VARCHAR2(9) , -- 雇员职位 atri_sal NUMBER(7,2) , -- 雇员工资 atri_comm NUMBER(7,2) , -- 雇员佣金 -- 此函数名称与父类函数名称一样,所以此处为函数的覆写 OVERRIDING MEMBER FUNCTION tostring RETURN VARCHAR2 ) ; / -- 定义Emp类体 CREATE OR REPLACE TYPE BODY emp_object AS OVERRIDING MEMBER FUNCTION tostring RETURN VARCHAR2 AS BEGIN RETURN '人员编号:' || SELF.atri_pid || ',姓名:' || SELF.atri_name || ',性别:' || SELF.atri_sex || '职位:' || SELF.atri_job || ',工资:' || SELF.atri_sal || ',佣金:' || SELF.atri_comm ; END ; END ; / -- 定义Student类规范,此类为Person子类 CREATE OR REPLACE TYPE student_object UNDER person_object( atri_school VARCHAR2(15) , atri_score NUMBER(5,2) , OVERRIDING MEMBER FUNCTION tostring RETURN VARCHAR2 ) ; / -- 定义Student类体 CREATE OR REPLACE TYPE BODY student_object AS OVERRIDING MEMBER FUNCTION tostring RETURN VARCHAR2 AS BEGIN RETURN '人员编号:' || SELF.atri_pid || ',姓名:' || SELF.atri_name || ',性别:' || SELF.atri_sex || '学校:' || SELF.atri_school || ',成绩:' || SELF.atri_score ; END ; END ; / |
person_object类中存在两个子类,即emp_object和student_object,而且这两个子类都覆写了person_object类中的tostring()函数; |
-
通过两个子类为person_object类对象实例化:
DECLARE v_emp person_object ; -- 声明person_object类对象 v_student person_object ; -- 声明person_object类对象 BEGIN -- 对象向上转型 v_emp := emp_object(7369,'SMITH','FEMALE','CLERK',800.0,0.0) ; v_student := student_object(7566,'ALLEN','FEMALE','MLDN',99.9) ; DBMS_OUTPUT.put_line('【雇员信息】' || v_emp.tostring()) ; DBMS_OUTPUT.put_line('【学生信息】' || v_student.tostring()) ; END ; / |
程序声明了person_object类的对象(v_emp,v_student),而后这两个对象分别通过不同的子类进行实例化,之后都可以使用父类对象进行接收,而在调用tostring()函数时执行的是不同类中覆写之后的操作 |
-
使用FINAL关键字
- 说明:可以利用FINAL关键字定义不能被继承的类与不能被覆写的函数;
-
示例1:使用FINAL定义的类不能被继承
-- 定义Person类规范 CREATE OR REPLACE TYPE person_object AS OBJECT ( atri_pid NUMBER ) FINAL ; -- 不管是否写此句默认均为FINAL / -- 定义Emp类规范,但是此时由于person_object类无法继承,所以出现错误 CREATE OR REPLACE TYPE emp_object UNDER person_object ( atri_job VARCHAR2(9) ) ; / |
此时的程序在创建emp_object子类时会出现"PLS-00590:正在尝试创建一个最终类型的子类型"错误提示消息,所以使用FINAL声明的类不能被集成 |
-
示例2:使用FINAL定义的函数不能被子类覆写
-- 定义Person类规范 CREATE OR REPLACE TYPE person_object AS OBJECT ( atri_pid NUMBER , FINAL MEMBER FUNCTION tostring RETURN VARCHAR2 ) NOT FINAL ; -- 不管是否写此句默认均为FINAL / -- 定义Emp类规范,但是此时由于person_object类无法继承,所以出现错误 CREATE OR REPLACE TYPE emp_object UNDER person_object ( atri_job VARCHAR2(9) , -- 错误:此处无法覆写tostring()函数 OVERRIDING MEMBER FUNCTION tostring RETURN VARCHAR2 ) ; / |
-
定义抽象函数
- 说明:抽象函数是指,类中的函数不希望被类对象直接使用,需要通过该类的子类来实现。在定义函数时,使用NOT INSTANTIABLE标记即可;同时,包含抽象函数所在的类也必须使用NOT INSTANTIABLE定义,这样的类也同样被称为抽象类
-
示例1:定义抽象类与抽象函数
DROP TYPE emp_object ; -- 定义Person类规范 CREATE OR REPLACE TYPE person_object AS OBJECT ( atri_pid NUMBER , -- 人员编号 atri_name VARCHAR2(10) , -- 人员姓名 atri_sex VARCHAR2(10) , -- 人员性别 NOT INSTANTIABLE MEMBER FUNCTION tostring RETURN VARCHAR2 -- 定义抽象方法 ) NOT FINAL NOT INSTANTIABLE ; -- 此处必须使用NOT INSTANTIABLE声明类 / -- 定义Emp类规范,此类为Person子类 CREATE OR REPLACE TYPE emp_object UNDER person_object ( atri_job VARCHAR2(9) , -- 雇员职位 atri_sal NUMBER(7,2) , -- 雇员工资 atri_comm NUMBER(7,2) , -- 雇员佣金 -- 此函数名称与父类函数名称一样,所以此处为函数的覆写 OVERRIDING MEMBER FUNCTION tostring RETURN VARCHAR2 ) ; / -- 定义Emp类体 CREATE OR REPLACE TYPE BODY emp_object AS OVERRIDING MEMBER FUNCTION tostring RETURN VARCHAR2 AS BEGIN RETURN '人员编号:' || SELF.atri_pid || ',姓名:' || SELF.atri_name || ',性别:' || SELF.atri_sex || '职位:' || SELF.atri_job || ',工资:' || SELF.atri_sal || ',佣金:' || SELF.atri_comm ; END ; END ; / |
本程序在person_object类中定义了一个tostring()的抽象函数,这个函数不能直接被person_object类的对象所使用,必须由该类的子类来实现。所以在定义emp_object类时明确地使用OVERRIDING关键字表示要覆写person_object类的tostring()函数 |
-
接上例:编写程序进行测试
DECLARE v_emp person_object ; BEGIN -- 通过子类对象为父类实例化 v_emp := emp_object(7369,'SMITH','FEMALE','CLERK',800.0,0.0) ; DBMS_OUTPUT.put_line(v_emp.tostring()) ; END ; / |