[Java] Output parameters

Pagina: 1
Acties:

  • Grimm9mm
  • Registratie: Maart 2000
  • Laatst online: 14-05 20:22
Hey mensen, ik zou willen weten of het mogelijk zou zijn om een method te schrijven die output-parameters zou hebben... omdat Java intern ook met pointers werkt zou zoiets toch mogelijk moeten zijn?
In de zin van:
code:
1
2
3
4
// x * y = z
void vermenigVuldig(Integer x,Integer y,Integer z) {
  //blabla
}

Iemand een creatief idee?

  • whoami
  • Registratie: December 2000
  • Laatst online: 04-09 22:16
Wat bedoel je met output parameters? Passing by reference?

Je kunt een vermenigvulding ook zo oplossen :
code:
1
2
3
4
int vermenigvuldig(int fact1, int fact2)
{
  return fact1 * fact2;
}

Ik denk trouwens dat passing by reference niet mogelijk is in Java, maar dat mogen de Javahova's komen verkondigen.
(Hoe kun je nu eigenlijk zo'n feature uit een taal laten? ;) )

https://fgheysels.github.io/


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 17:16
Ik ben bang dat dat niet mogelijk is. In Java worden objecten by reference en basic types by value doorgegeven. Als je argumenten objecten zijn, kun je ze wijzigen en zijn de resultaten in de aanroepende code te zien. Basic types worden naar de stack gekopieerd en deze wijzigen heeft dus geen effect op de originele variabelen.

edit:
Tweede punt klopt niet; ik had niet goed gelezen. Als je een mutable Integer klasse zou definiëren, zou jou methode wel werken.

  • tomato
  • Registratie: November 1999
  • Niet online
Bedoel je hoe Java denkt over passing by reference en by value :?

Als je hier op zoekt bij Google vind je genoeg info erover. Kijk overigens uit bij deze begrippen in Java. Dus wat wordt er by value doorgegeven, etc...

  • whoami
  • Registratie: December 2000
  • Laatst online: 04-09 22:16
Op donderdag 27 juni 2002 16:29 schreef Soultaker het volgende:

Wat zou er anders gebeuren als ik jou functie zo aanroep:
code:
1
  vermenigVuldig(1,2,3);

Constanten kunnen duidelijk niet overschreven worden.
Dit zou normaal gezien idd een compiler error moeten opleveren, omdat je constanten gebruikt. Maar als je variablen gebruikt, zijn dat geen constanten maar zou het geen error opleveren. Alleen zou het dan nog niet werken, om de reden die je aangaf -> de variablen worden naar de stack gekopieerd.

In C# heb je echter wel een mogelijkheid om ook de value-types (primitieven en structs) by reference door te geven. Daarvoor heb je het 'ref' keyword.

https://fgheysels.github.io/


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

De Integer is unmodifiable, maar hieronder een voorbeeld van wat jij wil.
code:
1
2
3
4
5
6
7
8
class ModifiableInteger{
    public int value;
}

...ergens in een ander object..
void inc(ModifiableInteger i){
  i.value++;
}

[edit]void vergeten bij inc methode :z

  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
Java kent geen out parameters. Je zult alle resultaten die je nodig hebt gewoon op moeten leveren in het resultaat van de functie. Op dit punt is het wel jammer dat Java geen ingebouwde notie van tuples heeft.

In een Corba applicatie die geschreven is in Java maken ze gebruik van 'holders' om de out en inout parameters na te bootsen (IDL support die namelijk wel). Zie bijvoorbeeld de "org.omg.CORBA.BooleanHolder".

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


  • Grimm9mm
  • Registratie: Maart 2000
  • Laatst online: 14-05 20:22
ik denk dat ik gewoon een callback-systeem ga gebruiken... het moet namelijk in combinatie met JavaNativeInterface gebruikt worden

  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
Dit komt trouwens doordat een Java methode nooit bij de stack-frames van andere methoden kan komen. Elke methode invocatie zorgt voor een stack-frame waarin lokale variabele en temporaries en dergelijke worden bijgehouden. Een Java methode kan in Java Bytecode echter alleen maar variabelen in zijn eigen stack-frame benaderen en dus niet in die van de aanroepende methode.

De enige manier om resultaten door te geven aan de aanroepende methoden is daarom via de heap (objecten) of via een methode resultaat.

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


  • Grimm9mm
  • Registratie: Maart 2000
  • Laatst online: 14-05 20:22
Dus java objecten zijn WEL passed by reference??? Hmm, even de Java Language Specification uitprinten.... 550Pags , dat zal mijn baas mr boonstra leuk vinden >:)

  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
Grimm9mm: Dus java objecten zijn WEL passed by reference???
De pointer naar een object wordt by value meegegeven aan de aanroepende methode. Je kunt hierdoor dus nooit een lokale variabele van de aanroepende methode laten wijzen naar een ander object.

In de 'volksmond' wordt dit vaak uitgelegd als dat de object by reference worden meegegeven. Dat is op zich een prima benadering, want je krijgt inderdaad een referentie naar hetzelfde object als de aanroepende methode.

Java kent strict genommen echter alleen maar pass by value omdat de pointer dus wel gekopieerd wordt. Dit heeft weer te maken met het feit dat een methode alleen maar lokaties in zijn eigen stack-frame kan benaderen. Als ook de pointer zelf by-reference zou worden meegegeven, kan je dus bij locaties in het stack-frame van de aanroepende methode. In C# en .NET IL kan dit wel, In C/C++ uiteraard ook, maar in Java niet.

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

Op donderdag 27 juni 2002 16:39 schreef Grimm9mm het volgende:
Dus java objecten zijn WEL passed by reference??? Hmm, even de Java Language Specification uitprinten.... 550Pags , dat zal mijn baas mr boonstra leuk vinden >:)
Dat hoef je niet op te zoeken hoor, hoe zou je anders een wijziging in een object kunnen maken als er altijd een copy wordt meegestuurd. En dan hebben we het nog niet eens over object aanmaak :D

  • Grimm9mm
  • Registratie: Maart 2000
  • Laatst online: 14-05 20:22
Hmm, hier ga ik even mee experimenteren...

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

Op donderdag 27 juni 2002 16:45 schreef Grimm9mm het volgende:
Hmm, hier ga ik even mee experimenteren...
daar durf ik jouw leven wel onder te verwedden..

ps: ik ben nog niet zo gek als jij er uit ziet ;)

succes ermee.

[edit] is ff melig ;)

  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
Alarmnummer: daar durf ik jouw leven wel onder te verwedden..
Zolang hij maar uit sun.misc.Unsafe blijft gaat alles goed :o .

Als hij Unsafe ontdekt (en dat nog steeds open ligt in de final, heb ik nog niet gecontrolleerd) vrees ik voor z'n leven als hij niet met een SecurityManager draait >:) .

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 17:16
Op donderdag 27 juni 2002 16:43 schreef mbravenboer het volgende:
In de 'volksmond' wordt dit vaak uitgelegd als dat de object by reference worden meegegeven. Dat is op zich een prima benadering, want je krijgt inderdaad een referentie naar hetzelfde object als de aanroepende methode.
Waarom is dit een prima benadering? Hoe zou je anders pass-by-reference kunnen implementeren? Op wat voor manier verschilt pass-by-reference in andere talen (C, C++, PHP, Perl, etc.) van de manier waarop Java dit implementeert?

  • Grimm9mm
  • Registratie: Maart 2000
  • Laatst online: 14-05 20:22
Ik had zoiets al eens eerder geprobeerd, maar toen zat ik met Integer te knoeien, en die zijn immers niet meer te veranderen na creatie... dom... had ik dus ff moeten checken in documentatie...

  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
Ah, het is nu toch afgesloten ;( >:) .
code:
1
2
3
4
5
6
7
8
public class Test {
  public static void main(String[] ps) {
    String osv = "Osvaldo";
    sun.misc.Unsafe.getUnsafe().putInt(osv, 16, 2);
    System.err.println(osv.length());
    System.err.println(osv);
  }
}

Resultaat:
code:
1
2
3
4
martin@linux:~ > java Test
Exception in thread "main" java.lang.SecurityException: Unsafe
      at sun.misc.Unsafe.getUnsafe(Unsafe.java:68)
      at Test.main(Test.java:4)

In de standaard Java libs zelf werkt dit wel omdat ze daar recht hebben op deze hele ranzinge constructies uit de onderwereld van Java.

Ook dit werkt niet ;)
code:
1
2
Exception in thread "main" java.lang.SecurityException: Prohibited package name: java.lang
      at java.lang.ClassLoader.defineClass(ClassLoader.java:495)

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


  • tomato
  • Registratie: November 1999
  • Niet online
mbravenboer: Ook dit werkt niet ;)
code:
1
Prohibited package name: java.lang
:D

  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
Soultaker: Waarom is dit een prima benadering?
Ik bedoelde dat dit wat mij betreft best by-reference genoemd mag worden, zolang het duidelijk is dat het om de objecten gaat. Java kent immers helemaal geen pass by-reference: argumenten worden altijd gekopieerd. Je geeft immers geen object mee aan een methode, maar een pointer naar een object.
Op wat voor manier verschilt pass-by-reference in andere talen van de manier waarop Java dit implementeert?
Strict genomen zou je pass-by-reference moeten reserveren voor de situatie waarin er een pointer wordt meegegeven naar een lokale variabele in het stack-frame van de aanroepende methode.

Dit zie je in C# dus zo terug (tijdje geleden gemaakt als voorbeeld hier op GoT, sorry voor de vage namen ;) ):
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
using System;

public class Voorwerp {

  public readonly int hoi;

  public Voorwerp (int invoer) {
    hoi = invoer;
  }
}

public class Test {
  public static void Main () {

    Voorwerp voorwerp = new Voorwerp(4);

    Console.WriteLine("Value: " + voorwerp.hoi);

    Value(voorwerp);
    Console.WriteLine("Value: " + voorwerp.hoi);

    Reference(ref voorwerp);
    Console.WriteLine("Value: " + voorwerp.hoi);
  }

  public static void Value(Voorwerp voorwerp) {
    voorwerp = new Voorwerp(6);
  }

  public static void Reference(ref Voorwerp voorwerp) {
    voorwerp = new Voorwerp(6);
  }
}

Dit is 'echte' pass-by-reference (uiteraard had ik ook C of C++ voorbeelden kunnen geven).

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 17:16
Op donderdag 27 juni 2002 17:13 schreef mbravenboer het volgende:
Strict genomen zou je pass-by-reference moeten reserveren voor de situatie waarin er een pointer wordt meegegeven naar een lokale variabele in het stack-frame van de aanroepende methode.

Dit zie je in C# dus zo terug (tijdje geleden gemaakt als voorbeeld hier op GoT, sorry voor de vage namen ;) ):
Ah, het voorbeeld maakt je punt inderdaad duidelijk.
code:
1
2
3
  public static void Reference(ref Voorwerp voorwerp) {
    voorwerp = new Voorwerp(6);
  }
Vanuit mijn (oorspronkelijke) oogpunt wordt hier dus een reference by-reference verstuurd. Naar mijn idee wordt een object by-value gepassed (wat in veel talen ook kan) als de data van dat object op de stack geplaatst wordt. In C# is dat blijkbaar niet mogelijk.

Officieel zou een object dat by-value wordt gepassed natuurlijk met een copy-constructor geïnitialiseerd moeten worden, dus objecten hetzelfde behandelen als andere stukken data kan niet echt.

Maar ik kan me ook wel in jouw standpunt vinden. Je zou een basic type (conceptueel!) ook als klasse kunnen zien, waarbij bijvoorbeeld '3' een instantie van een integer-klasse is.

Vanuit dat oogpunt gezien werken de volgende twee functies inderdaad exact hetzelfde:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
void f(int x)
{
  x = 3;
}

void f(Klasse k)
{
  k = new Klasse(456);
}

int a=1;
Klasse k=new Klasse(123);
f(a); // wijzigt a niet
f(k); // wijzigt k niet

Dat hoef ik jou natuurlijk niet te vertellen, maar goed, dan heb ik in ieder geval m'n eigen gedachten weer op een rijtje. ;)

  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
Ik ben even heel erg stout geweest :o .
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import sun.misc.Unsafe;
import java.lang.reflect.Field;

public class Test {
  public static void main(String[] ps) {
    try {
    Class clazz = Unsafe.class;

    Field field = clazz.getDeclaredField("theUnsafe");
    field.setAccessible(true);
  
    Unsafe unsafe = (Unsafe) field.get(null);
  
    String osv = "Osvaldo";
    unsafe.putInt(osv, 16, 2);
  
    System.err.println(osv.length());
    System.err.println(osv);
    } catch(Exception exc) {
    exc.printStackTrace();
    }
  }
}

Uitvoer:
code:
1
2
3
martin@linux:~ > java Test
2
Os

* mbravenboer duikt weg :+ .

Uiteraard kan dit niet in een Sandbox: daar kan je private fields niet Accessible setten.

Overigens heb ik hier geen decompilatie voor nodig gehad 8-) .

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
Soultaker: Vanuit dat oogpunt gezien werken de volgende twee functies inderdaad exact hetzelfde
Exact, das een mooi en eenvoudig voorbeeld :) .

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


  • Xanthus
  • Registratie: Februari 2002
  • Laatst online: 11-07 12:45
Op donderdag 27 juni 2002 17:34 schreef mbravenboer het volgende:
Ik ben even heel erg stout geweest :o .
Waarom kan het eigenlijk normaal niet? Als een programmeur een computer kan wissen moet hij vind ik ook best immutable objecten kunnen veranderen :)

  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
Xanthus: Waarom kan het eigenlijk normaal niet?
Voor Strings geldt in het bijzonder dat ze geshared kunnen worden omdat ze constanten zijn, maar ik denk eigenlijk niet dat de direct reden is geweest om alle wrappers immutable te maken.

Ik denk eerder dat het te maken heeft met de vergelijking met primitieven: als je een variable met als type int, long, double etc meegeeft aan een methode:
code:
1
2
int x = 3
doSomething(x);

kan deze methode de waarde van de variabele x niet aanpassen.

Stel dat een Integer mutable zou zijn, dan zou dit wel kunnen:
code:
1
2
3
4
5
6
Integer x = new Integer(3);
doSomething(x);

public void doSomething(Integer x) {
  x.setValue(5);
}

Ik verwacht eigenlijk in dat de compiler in de toekomst een stukje analyse zal gaan doen en het onderscheid tussen primitieven en objecten weg zal vallen. Omdat de wrappers 1 op 1 mappen op primitieve typen maakt het namelijk niet uit welke van de twee je gebruikt. Je kan hierdoor de primitieven gewoon weggooien en de compacte syntax in feite laten staan voor echte objecten. Uiteraard gaat dit hele waardeloze performance opleveren omdat je in dat geval overal met objecten werkt die voortdurend garbage zullen veroorzaken. Een slimme compiler zal echter kunnen nagaan of de objecten misschien vervangen kunnen worden door de oude primitieven.

Merk wel op dat dit iets heel anders is dan auto-boxing in C#. In C# wordt een primitieve naar behoefte omgezet in een object: zodra een primitieve als een object wordt gebruikt, wordt hij ingepakt in een box. Zodra hij weer als een object wordt gebruikt, wordt hij weer uitgepakt.

Mijn voorstel is anders: het gebruike van echte primitieven is hier een optimalisatie detail, wat volledig afgehandeld wordt door de compiler. Alles is gewoon altijd een object (merk op dat dat iets essentieel anders is dan dat iets altijd omgezet kan worden in een object) en de compiler analyseert of dit misschien efficient met behulp van echte primitieven kan worden gecompileerd.

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 17:16
Op donderdag 27 juni 2002 22:35 schreef Xanthus het volgende:
Waarom kan het eigenlijk normaal niet? Als een programmeur een computer kan wissen moet hij vind ik ook best immutable objecten kunnen veranderen :)
Uiteraard zijn mbravenboer's observaties met betrekking tot optimalisatie ook geldig, maar ik denk dat de principiële onderbouwing belangrijker is. (Ik vind dat 'net' ontwerp in principe boven efficiëntie moet gaan, al is 't mooi als ze te combineren zijn.)

Een object representeert een zekere instantie van een klasse. Het resultaat van 'new Integer(5)' is een object dat het getal 5 representeert. Als dit object van waarde verandert, neemt het object een hele andere identiteit aan. Het lijkt in niets meer op het object dat 5 representeerde. In een programmeertaal zou dit tot uiting moeten komen in 't feit dat het niet alleen in waarde een ander object is, maar ook in identiteit (de pointer- of referencewaarde) dus.

Vanuit dit oogpunt zou een instantie van een klasse gedurende zijn levensloop slechts een enkel 'ding' mogen voorstellen. Hierdoor is het duidelijk dat een object altijd 'zichzelf' blijft en eventuele wijzigingen in de staat verscholen moeten worden achter methoden.

Er zit hierbij een subtiel verschil tussen de 'staat' en de 'identiteit' van het object. Een String object representeert uitsluitend een reeks van karakters en heeft verder geen staat. Welke reeks van karakters dit is, bepaalt de identiteit van het object.

Een venster representeert diverse eigenschappen van dat venster. Wanneer je je browservenster resized, blijft het conceptueel hetzelfde venster. Alle verwijzingen naar dat venster (in je taakbalk enzo) blijven intact, aangezien de identiteit van het venster niet gewijzigd is. De staat van het venster (de eigenschappen, waaronder de grootte en positie), zijn wel gewijzigd.

Wat mij betreft heeft in het volgende voorbeeld de tweede constructie (die ontleent is aan SmallTalk, waarin geen primitieven bestaan en getallen dus ook objecten zijn) de voorkeur:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
class Integer
{
  private int value;

  public Integer(int value)
    { this.value = value; }

  public Integer add_v1(ie)
    { this.value += other.integer; return this; }

  public Integer add_v2(Integer other)
    { return new (this.value + other.value); }
}

Een gevolg van de tweede versie is dat er meer objecten gecreeërd worden, wat ten behoeve van de efficiëntie natuurlijk niet zo handig is. Zoals mbravenboer al aangaf, staat het de compiler (zoals altijd) vrij om deze constructie zo te optimaliseren dat er géén nieuw object wordt aangemaakt, als dat geen gevolgen heeft voor de werking van het programma.

Bij complexere typen is het overigens zo, dat het onmogelijk maken van wijzigingen (en dus het stimuleren van het maken van nieuwe instanties voor conceptueel andere objecten) de code veel simpeler houdt, omdat elk object de constructor en destructor (al is die in Java wat minder duidelijk aanwezig dan bijvoorbeeld C++) precies één keer aangeroepen worden.

Wanneer een object gewijzigd kan worden, moet tussendoor (een deel van) de destructor uitgevoerd worden, gevolgd door (een deel van) de constructor, waarbij gegarandeerd moet worden dat ondertussen het object niet door andere objecten gebruikt wordt (aangezien het object tijdens dit proces niet noodzakelijkerwijs correct opgebouwd is). Hierdoor moet veel meer code geschreven worden (omdat delen van de constructor en destructor ook in de methoden terugkomen) en het garanderen van de complexheid van de klasse wordt veel ingewikkelder.

Zeker in wat meer low-level talen als C++ is het consistent houden van objecten ingewikkeld. Het is daarom praktisch om elk object slechts één levenscyclus te geven en hooguit aggregatierelaties door de gebruiker te laten wijzigen. Meestal is veel meer ook niet nodig.
Pagina: 1