Как цепочка вызовов в объектном типе pl / sql функций, возвращающих SELF
Я хочу, чтобы объект oracle возвращал себя и мог связывать эти вызовы. Как мне это сделать?
Я пытался вернуть тот же тип, но он не работает, я также пытался добавить процедуру, которая вызывается функцией, но она тоже не работает. Всегда жалуется на изменение значения элемента width. Похоже, функции не допускают побочных эффектов?, они смоделированы по более математическому принципу функции? Достижимо ли это?. Думаю, я мог бы написать следующее: функция таким образом, он строит новый прямоугольник с самим собой, но это так много работы.
Моя цель состоит в том, чтобы иметь возможность связывать вызовы, такие как jQuery или некоторые классы java (синглет?). Что-то вроде:
r := r.setWidth(0).setWidth(1).setWidth(2);
Конечно, у него было бы больше методов, и он не был бы прямоугольником. Это ошибка:
Error: PLS-00363: expression 'SELF' cannot be used as an assignment target
Line: 18
Text: stWidth(w);
-
CREATE OR REPLACE TYPE rectangle AS OBJECT
(
-- The type has 3 attributes.
length NUMBER,
width NUMBER,
area NUMBER,
-- Define a constructor that has only 2 parameters.
CONSTRUCTOR FUNCTION rectangle(length NUMBER, width NUMBER)
RETURN SELF AS RESULT,
MEMBER FUNCTION setWidth(w NUMBER) RETURN rectangle,
MEMBER PROCEDURE stWidth(w NUMBER)
)
-
CREATE OR REPLACE TYPE BODY rectangle AS
CONSTRUCTOR FUNCTION rectangle(length NUMBER, width NUMBER)
RETURN SELF AS RESULT
AS
BEGIN
SELF.length := length;
SELF.width := width;
-- We compute the area rather than accepting it as a parameter.
SELF.area := length * width;
RETURN;
END;
MEMBER PROCEDURE stWidth(w NUMBER) IS
BEGIN
self.width := w;
END;
MEMBER FUNCTION setWidth(w NUMBER) RETURN rectangle IS
BEGIN
stWidth(w);
RETURN SELF;
END;
END;
Заранее благодарю.
4 ответов:
Вы не можете одновременно изменять объект и назначать ему назначение. Вы уже знаете решение, "построить новый прямоугольник с самим собой". Но это не будет большой работой.
Заменить это:
MEMBER FUNCTION setWidth(w NUMBER) RETURN rectangle IS BEGIN stWidth(w); RETURN SELF; END;С этим:
MEMBER FUNCTION setWidth(w NUMBER) RETURN rectangle IS v_rectangle rectangle := self; BEGIN v_rectangle.width := w; RETURN v_rectangle; END;На самом деле вы получили ошибку компиляции. По умолчанию
SELFявляется параметромIN. ВызовstWidthне удался, так как он изменял параметрINс помощьюself.width := w;.См.: http://docs.oracle.com/cd/B28359_01/appdev.111/b28371/adobjbas.htm#CHDCFEEE
Я всегда первый параметр, передаваемый в метод.
В функциях-членах, если SELF не объявлен, его параметр mode по умолчанию-IN.
В процедурах-членах, если SELF не объявлен, его режим параметров по умолчанию-IN OUT. Поведение по умолчанию не включает NOCOPY подсказка компилятора.
Извините, я немного опоздал, но другие ответы неверны, по крайней мере, с помощью Oracle 11gR2, и то, что вы пытаетесь достичь, вполне возможно.
Я только недавно наткнулся на эту проблему, и я вижу, что можно окончательно вернуть ссылку наSELFточно так же, как вы пытались, не поддаваясь никаким компромиссам или применяя обходные пути.Единственное, что нужно, - это переопределить метод, (явно) задав (неявно)
SELFпараметр asSELF IN OUT rectangle.SELFявляется ведущим параметром, который молча передается каждому объектному методу, а для функций определяется какIN(неизменяемый; это то, что предположительно заставляет компилятор жаловаться). Это вроде как установлено во время компиляции, но хорошая часть заключается в том, что когда вызывает метод, вы можете его опустить.
В Примере в конце поста (слегка переписанном с Вашего) мы определяемMEMBER FUNCTION incrementWidth(SELF IN OUT rectangle, w NUMBER) RETURN rectangleИ мы выполняем его, опуская
SELFСсылка:Обратите внимание, что есть два предупреждения, о которых следует помнить.declare r rectangle := rectangle(1,2); begin dbms_output.put_line('width is: ' || r.width); dbms_output.put_line('new width is: ' || r.incrementWidth(3).width); end; /Предупреждение 1
Каждый вызов метода временно создает новую копию объекта.
Но только временно, новый экземпляр недолговечен, как раз между началом и концом метода. Это присуще использованию параметровIN OUTдля всех функций или процедур и не является специфичным для типов объектов. Если вы хотите предотвратить такое поведение, вы можете переопределить подпись вашей функции с помощью подсказкиNOCOPY:MEMBER FUNCTION incrementWidth(SELF IN OUT NOCOPY rectangle, w NUMBER) RETURN rectangleСмотрите ORACLE-BASE - NOCOPY для получения более подробной информации. Обратите внимание, что этоподсказка , которая не гарантирует, что Вы, наконец, используете ту же ссылку на объект, а не вновь созданный объект, поэтому используйте с осторожностью.
Предупреждение 2
Учитывая, что вы подняли этот вопрос, есть вероятность, что у вас есть фон ООП, и вы можете получить сюрприз при попытке вызвать метод без использования возвращенная ссылка выглядит следующим образом
r.incrementWidth(10);Компилятор вернет ошибку:
Так что же здесь происходит? Ну а так называемый "статический полиморфизм" (то есть выбор метода перегрузки при компиляции) в pl/sql несколько отличается от других языков ООП, поскольку учитывает даже использование возвращаемого типа. Чтобы решить эту проблему, добавьте сопутствующую процедуру с подписью, различие которой заключается только в отсутствии возвращаемого тип:PLS-00221: 'INCREMENTWIDTH' is not a procedure or is undefinedMEMBER FUNCTION incrementWidth(SELF IN OUT rectangle, w NUMBER) RETURN rectangle, MEMBER PROCEDURE incrementWidth(SELF IN OUT rectangle, w NUMBER)Разумно, если вы не хотите дублировать один и тот же код в функции и процедуре, процедура будет внутренне делегировать функцию; и в зависимости от версии Oracle, которую вы используете, вы можете захотеть поиграть с подстановкой кода (см. OCP: More New PL/SQL Features), чтобы достичь той же скорости, что и копируемая реализация (вряд ли вы заметите реальную разницу). Явная "подстановка" указывает на метод по имени, однако она работает и в этом случае где имя метода перегружено.
В приведенном ниже примере вы увидите, что либо функция, либо процедура вызываются альтернативно в зависимости от использования возвращаемого/not_returned параметра.
Итак, наконец-то...
Код, который можно написать, следующий (я не использовал
NOCOPY, чтобы не загрязнять соответствующие материалы, но это просто сделать)CREATE OR REPLACE TYPE rectangle AS OBJECT ( length NUMBER, width NUMBER, CONSTRUCTOR FUNCTION rectangle(length NUMBER, width NUMBER) RETURN SELF AS RESULT, MEMBER FUNCTION incrementWidth(SELF IN OUT rectangle, w NUMBER) RETURN rectangle, MEMBER PROCEDURE incrementWidth(SELF IN OUT rectangle, w NUMBER) ); / CREATE OR REPLACE TYPE BODY rectangle AS CONSTRUCTOR FUNCTION rectangle(length NUMBER, width NUMBER) RETURN SELF AS RESULT AS BEGIN SELF.length := length; SELF.width := width; RETURN; END; MEMBER FUNCTION incrementWidth(SELF IN OUT rectangle, w NUMBER) RETURN rectangle IS BEGIN dbms_output.put_line('...invoking the function with input ' || w); width := width + w; RETURN SELF; END; MEMBER PROCEDURE incrementWidth(SELF IN OUT rectangle, w NUMBER) IS BEGIN PRAGMA INLINE (incrementWidth, 'YES'); dbms_output.put_line('...invoking the procedure with input ' || w || ', that in turn is...'); self := incrementWidth(w); END; END; /При исполнении...
set serveroutput on select * from v$version where rownum = 1; declare r rectangle := rectangle(1,2); begin dbms_output.put_line('width is: ' || r.width); --this is invoking the "function" version, because we are making use of --the returned rectangle object dbms_output.put_line('new width is: ' || r.incrementWidth(3).width); --the original reference has been updated even without using the NO COPY hint dbms_output.put_line('original object has width updated: ' || r.width); --this is invoking the "procedure" version, because we are not using the returned object r.incrementWidth(3); --of course this has finally worked as well dbms_output.put_line('again what is the new width like now?: ' || r.width); end; /Вы получаете
BANNER -------------------------------------------------------------------------------- Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production width is: 2 ...invoking the function with input 3 new width is: 5 original object has width updated: 5 ...invoking the procedure with input 3, that in turn is... ...invoking the function with input 3 again what is the new width like now?: 8
Вы не можете вернуть из функции-члена. Вы можете создать копию,но я не уверен, почему функция setwidth возвращает объект rectangle. Я знаю, что этот базовый пример, вероятно, взят из некоторых старых документов Oracle, но я бы не стал помещать вычисляемое поле (область) в качестве атрибута. Если он вычисляется, то это должна быть функция-член (или процедура). Причина в том, что если вы не хотите всегда помнить об обновлении области в функциях, которые влияют на область, вы выстрелите себе в ногу. Ваш пример таков нарушено (кроме setWidth, возвращающего self), , так как только конструктор вычисляет область.
set serveroutput on declare r rectangle := rectangle(3,2); begin dbms_output.put_line('Area is: ' || r.area); -- change the width r.stWidth(4); dbms_output.put_line('Area is: ' || r.area); end;Вывод:
Area is: 6 Area is: 6Очевидно, что это неправильно. Я бы сделал что-то вроде:
CREATE OR REPLACE TYPE rectangle AS OBJECT ( -- The type has 2 attributes. len NUMBER, width NUMBER, -- Define a constructor that has only 2 parameters. CONSTRUCTOR FUNCTION rectangle(len NUMBER, width NUMBER) RETURN SELF AS RESULT, MEMBER PROCEDURE setLength(l NUMBER), MEMBER PROCEDURE setWidth(w NUMBER), MEMBER FUNCTION getArea return NUMBER, MEMBER PROCEDURE showArea ); CREATE OR REPLACE TYPE BODY rectangle AS CONSTRUCTOR FUNCTION rectangle(len NUMBER, width NUMBER) RETURN SELF AS RESULT AS BEGIN SELF.len := len; SELF.width := width; RETURN; END; MEMBER PROCEDURE setLength(l NUMBER) IS BEGIN self.len := l; END; MEMBER PROCEDURE setWidth(w NUMBER) IS BEGIN self.width := w; END; MEMBER FUNCTION getArea return NUMBER IS BEGIN return self.len * self.width; END; MEMBER PROCEDURE showArea IS BEGIN -- Just shows how we calculated area and spits to console dbms_output.put_line('Area is: ' || self.getArea || ' (' || self.len || ' * ' || self.width || ')'); END; END;И таким образом вы получите:
set serveroutput on declare r rectangle := rectangle(3,2); begin dbms_output.put_line('Area is: ' || r.getArea); -- change the width r.setWidth(4); dbms_output.put_line('Area is: ' || r.getArea); end;Вывод:
Area is: 6 Area is: 12
Я столкнулся с той же проблемой, и так как лучшие ответы не совпадают, мне пришлось экспериментировать самому.
У меня есть следующий код, который не будет работать, несмотря на то, что ответ Антонио говорит:
create or replace type tst as object ( inner_value varchar2(100), constructor function tst return self as result, member function update_value( self in out nocopy tst, p_value varchar2) return tst, member procedure print_value(self in out nocopy tst) ) final; / create or replace type body tst as constructor function tst return self as result is begin self.inner_value := 'DEFAULT'; return; end; member function update_value( self in out nocopy tst, p_value varchar2) return tst is begin self.inner_value := p_value; return self; end; member procedure print_value(self in out nocopy tst) is begin dbms_output.put_line(self.inner_value); end; end; /Следующий код не будет выполняться независимо от того, определен ли параметр
selfс помощьюnocopyили нет.set serveroutput on; begin tst().update_value('TEST').print_value; end; /Если
print_valueпереопределено какmember procedure print_value(self in tst), тоtst().print_value;становится допустимым утверждением.Конструктор ѕневозможно быть соединены, если параметр self является
in outилиin out nocopy, независимо от того, возвращаете ли вы копию илиself, вы всегда получитеPLS-00363. Единственный способ выполнить вложенные вызовы конструктора-использовать параметрыself inвезде и сделать копию, что разочаровывает:create or replace type tst as object ( inner_value varchar2(100), constructor function tst return self as result, member function update_value( self in tst, p_value varchar2) return tst, member procedure print_value(self in tst) ) final; / create or replace type body tst as constructor function tst return self as result is begin self.inner_value := 'DEFAULT'; return; end; member function update_value( self in tst, p_value varchar2) return tst is a_copy tst; begin a_copy := tst; a_copy.inner_value := p_value; return a_copy; end; member procedure print_value(self in tst) is begin dbms_output.put_line(self.inner_value); end; end; /Тогда это выводит
TESTкак и ожидалось:set serveroutput on; begin tst().update_value('TEST').print_value; end; /Если вы можете разделить оператор на
t := tst(); t.update_value('TEST').print_value;, то он работает даже сin out nocopyselfпараметры.
Comments