当包体中有两个名称相同但参数不同的函数时,这意味着什么?



在软件包规范中,函数名称只出现一次,因此不会重载。在包正文中,相同的函数名称使用不同的参数集出现两次。其中一个与包规范中的参数相同。我的问题是,包体中的"第一个函数"是否真的被调用了,如果是这样,它到底在做什么?它正试图回归自己。

--package specification
CREATE OR REPLACE 
PACKAGE jtestpkg
IS
FUNCTION testfunc(p_num IN NUMBER, p_out1 IN out varchar2, p_out2 IN out varchar2)
RETURN NUMBER;
END jtestpkg;
--package body
CREATE OR REPLACE 
PACKAGE BODY jtestpkg
IS
--first func
function testfunc
(p_num IN NUMBER,
p_out1 IN OUT varchar2) 
return number is
v_out2 varchar2(50);
BEGIN
dbms_output.put_line('invoking first func');
RETURN testfunc(
p_num,
p_out1,
v_out2);
END;
--second func
FUNCTION testfunc(
p_num IN NUMBER,
p_out1 IN OUT varchar2,
p_out2 IN OUT varchar2)
RETURN NUMBER
IS
v_num number;
BEGIN
IF 1=p_num THEN
p_out1:='FirstOUT_1';
p_out2:='SecondOUT_1';
dbms_output.put_line(v_num||p_out1||p_out2);
RETURN 1;
elsif 2=p_num THEN
p_out1:='FirstOUT_2';
p_out2:='SecondOUT_2';
dbms_output.put_line(v_num||p_out1||p_out2);
RETURN 2;
ELSE
p_out1:='FirstOUT_3';
p_out2:='SecondOUT_3';
dbms_output.put_line(v_num||p_out1||p_out2);
return 3;
END IF;
------
p_out1:='FirstOUT_0';
p_out2:='SecondOUT_0';
dbms_output.put_line(v_num||p_out1||p_out2);
RETURN 0;
END testfunc;
END jtestpkg;

规范中声明的函数是公共的,可以从包外部调用。在主体中定义但未在规范中声明的函数是私有的,只能从该包中调用。

在您的示例中,包体中函数的第二个重载版本(已标记为"second func")与规范中的声明匹配,因此这是从其他地方调用函数时涉及的版本:

declare
rc number;
in_out_1 varchar2(20) := 'A';
in_out_2 varchar2(20) := 'B';
begin
rc := jtestpkg.testfunc(42, in_out_1, in_out_2);
end;
/
FirstOUT_3SecondOUT_3

PL/SQL procedure successfully completed.

您体内的第一个重载函数(您标记为"first func")在规范中没有匹配的声明,因此您无法在外部调用它:

declare
rc number;
in_out_1 varchar2(20) := 'A';
begin
rc := jtestpkg.testfunc(42, in_out_1);
end;
/
ORA-06550: line 5, column 9:
PLS-00306: wrong number or types of arguments in call to 'TESTFUNC'
ORA-06550: line 5, column 3:
PL/SQL: Statement ignored

我的问题是,包体中的"第一个函数"是否真的被调用

不。在您的代码中,"第一个函数"永远不会被调用。

它正试图回归自己。

不,不是。如果您的"第一个函数"本身是从包内的其他地方调用的,则会调用"第二个函数",但您当前没有这样做。

那里的调试说"调用第一个函数",但事实并非如此,它正在调用第二个函数,因为它进行的调用有三个参数 - 与"第二个函数"参数列表匹配。(这恰好是公开的,但如果不是也没关系,因为它无论如何都是包的内部)。

举个例子,你可以调用私有函数作为包实例化和初始化的一部分:

...
END testfunc;
-- initialization, called on instantiation (for each session)
BEGIN
dbms_output.put_line('Initialization start');
declare
rc number;
in_out_1 varchar2(20) := 'A';
begin
dbms_output.put_line('Initialization: calling first func');
rc := testfunc(1, in_out_1);
end;
dbms_output.put_line('Initialization end');
END jtestpkg;
/

然后,在会话中首次调用包中的任何公共内容都会实例化它,从而对其进行初始化,从而运行该包级块。所以使用相同的匿名块:

declare
rc number;
in_out_1 varchar2(20) := 'A';
in_out_2 varchar2(20) := 'B';
begin
rc := jtestpkg.testfunc(42, in_out_1, in_out_2);
end;
/

您会看到(仅限会话中的第一次):

Initialization start
Initialization: calling first func
invoking first func
FirstOUT_1SecondOUT_1
Initialization end
FirstOUT_3SecondOUT_3

PL/SQL procedure successfully completed.

您仍然会看到与以前相同的FirstOUT_3SecondOUT_3输出,来自该调用中传递的值 42;但在此之前,您会看到来自"第一个功能"调用"第二个功能"的输出FirstOUT_1SecondOUT_1,值为 1,作为该初始化过程的一部分。


允许函数调用自身,即递归调用,但它每次都需要更改调用,否则它会陷入无限循环,最终被杀死。不过,你在这里也没有这样做。

最新更新