Não estou querendo gerar um flame,
mas vou defender meu ponto de vista.
tecnicamente o Java não implementa completamente o mecanismo de passagem de parâmetros, se aplicado o conceito literal. Na realidade, esse mecanismo foi literalmente copiado da linguagem C, vejamos:
Parâmetros, segundo Carlo Ghezzi e Mehdi Jazayeri em Conceitos de Linguagem de Programação, são informações de entrada, saída ou entrada/saída para rotinas e funções.
Segundo os mesmos, existem dois tipos de parâmetros:
- Formal (Parâmetro) - É aquele que, na definição do módulo, está na lista de parâmetros. Este parâmetro não é a variável em si, mas apenas um sinônimo ao qual o módulo fará referência.
- Real (argumento) - É auqle que, na chamada ao módulo, está na lista de parâmetros. Este parâmetro é a variável que envia e/ou recebe informações do módulo.
Ainda o autor, afirma, ter duas formas de passagem de parâmetro:
- Por valor: O conteúdo da variável é passado para o módulo. Só permite a leitura da variável, sendo que o parâmetro formal define uma variável local.
- Por referência: O endereço da variável é passada para o módulo. Permite tanto a leitura quanto a alteração do conteúdo da variável.
Isso mesmo para as linguagens de programação estruturadas são implementadas de várias formas e algumas jamais chegaram a implementar se quer parcialmente este conceito. Por exemplo o COBOL que possui apenas uma área de memória compartilhada por todos os módulos (DATA DIVISION).
A própria linguagem C não possui um identificador para classificar a forma da passagem de parâmetro, e mesmo porque ela implementa única e exclusivamente a passagem de parâmetro por valor, quando se precisa passar o parâmetro por referência em C, faz se uso de ponteiros. Algo similar (para não falar idêntico) ao mecanismo utilizado pelo Java.
Naturalmente, a implementação do Pascal (para o modelo de linguagem estruturada) é uma das mais formais e aplica este conceito na integra. Não devemos esquecer que a linguagem Pascal foi criada para ensinar os programados a programar de forma estruturada e evitar as más práticas da época, como os comandos de desvio.
Tecnicamente, já expliquei anteriormente como as linguagens implementam a passagem de parâmetro e os respectivos modelos de memória, transcrevo a seguir:
a memória em praticamente todas as linguagens são dividias em três segmentos: código, heap e stack.
A área de código é onde fica os programas propriamente ditos.
O heap e o stack em java trabalham da seguinte forma:
Todos os tipos primitivos são alocados no stack (como se fossem variáveis locais e são).
Todos os objetos sempre são alocados no heap e a variável que os referência no stack.
fonte:
http://www.guj.com.br/posts/list/46065.java
Agora se para as linguagens estruturadas é controverso a implementação deste conceito, como ficam as linguagens do paradigma Orientado a Objetos em que classificam os argumentos apenas como “mensagens” sendo trocadas pelos/entre os objetos.
Para Yourdon e Coad, uma mensagem significa: “Um princípio para administração da complexidade - notadamente para interfaces - é a comunicação com mensagens”.
Bom neste ponto, faz-se necessário questionar: “se em java não existissem tipos primitivos, permaneceria a dúvida e a discussão com relação a forma de passar as mensagens?”
Eu acredito que não. Porque ao copiarmos ou clonarmos um objetos (recurso necessário para passagem por valor), estaremos sempre produzindo dois objetos com o mesmo conteúdo, mas serão sempre dois objetos. O que difere de termos duas ou mais referências para o mesmo objeto.
Vamos por, os conceitos em prática para explicar melhor o que estou querendo demonstrar:
void swap(int a, int b) {
int aux = a;
a = b;
b = aux;
}
Aqui é fácil demonstrar e de aceitar que os tipos primitivos são passados por valor. Mas e aqui:
void metodoQualquer(StringBuilder sb) {
sb.append(" sufixo ");
}
public static void main(String[] args) {
StringBuilder s = new StringBuilder("casa ");
System.out.println(s);
metodoQualquer(s);
System.out.println(s);
}
Alguém pode afirmar que aconteceu uma passagem de parâmetro por valor? É possível afirmar que ocorreu uma cópia do conteúdo do objeto representado por s na chamada do método “metodoQualquer” e quando o mesmo foi finalizado a variável s permaneceu com o mesmo conteúdo.
Naturalmente que não. Mas então foi passagem por referência?
Se aplicarmos a definição ao pé da letra, a resposta continua sendo não. Porque java copia o endereço do objeto e não da variável, mas o efeito obtido para o objeto é equivalente a passagem por referência no que tange a possibilidade de ler e modificar o conteúdo.
Tanto que podemos afirmar: O objeto passado para um método poderá ser lido e ter seu conteúdo modificado tanto quanto o nível de acesso a seus recursos (propriedades e métodos) permitirem.
Naturalmente, modificar o conteúdo de um objeto não é criar uma nova instância e associar essa instância a referência original (ao argumento passado ao método, a variável original).
E por quê os objetos não são passados por valor? Resposta direta: por causa do custo de memória e do tempo de processamento para realização de operações de clonagem.
E por quê em java não existe passagem por referência?
Primeiro temos que ver que os objetos já são passados por referência (o endereço deles são copiados dos argumentos para os parâmetros dos métodos - no exemplo de s para sb), apenas o endereço das variáveis que faz (mantém) a referência ao objeto não é passada.
Segundo que se java implementasse a passagem de parâmetros por referência dos tipos primitivos ela teria que tratar os problemas e complexidade dos vazamentos de memória que ocorrem nas pilhas do C/C++. O ganho cabaria sendo muito pequeno perto da complexidade de administrar uma realocação ou redimensionamento de uma informação preservada no stack.
Devemos lembrar que em java, todos os objetos (inclusive os vetores de tipos primitivos) são mantidos no heap.
Portanto senhores, se forem perguntados tecnicamente se java passa por parametros por valor ou por referência, devemos afirmar que o mecanismo de passagem de parâmetros é sempre por valor, porém o efeito obtido para objetos são equivalentes da passagem por referência.
A afirmação da SUN com relação a isso esta descrita em: [url]http://java.sun.com/docs/books/tutorial/java/javaOO/arguments.html{/url]
Passing Primitive Data Type Arguments
Primitive arguments, such as an int or a double, are passed into methods by value.
(…)
Passing Reference Data Type Arguments
Reference data type parameters, such as objects, are also passed into methods by value. This means that when the method returns, the passed-in reference still references the same object as before. However, the values of the object’s fields can be changed in the method, if they have the proper access level.
No Delphi, o problema equivalente também existe e a implementação da linguagem apresenta problemas similares. Por exemplo, quando um objeto é passado por valor ele não é clonado e todas as alterações são refletidas no mesmo objeto. A diferença é que na passagem por referência ele permite criar novas instâncias e associar a variável do método chamador. Diga-se de passagem, a própria Borland declarou que isso possibilitava erros de alocação de memória, uma vez que quando o programador criasse uma nova instância sem liberar a antiga, a antiga permaneceria em memória (ausência do mecanismo de gc).
A sim, acredito que não menos relevante, seja o funcionamento de uma operação de swap entre dois objetos:
Supomos que temos os objetos A e B, a operação de swap seria fazer com que o conteúdo de A fosse para B e de B para A, então a implementação correta para isso seria algo assim:
void swap(Object a, Object b) {
Object aux = a.clone();
a.assign( b );
b.assign( aux );
}
E com relação a isso:
P.S. Pela mesma ordem de ideias que faz as pessoas distinguir passagem de primitivos e de objetos é comum
se ouvir falar em colocar um final no argumento para que o argumento não possa ser alterado.
Isso é irrelevante já que a variável original nunca será modificada. Colocar final pode ser util para o programador muito iniciante
mas é um erro conceptual porque alterar a variável não provoca nenhum efeito fora do método e , muitas vezes, é interessante reaproveitar a variável de argumento.
Novamente eu tenho de discordar, apesar do efeito final poder ser o que sergiotaborda descreveu, a utilização de final ma declaração evita a criação implicita de novas instância a atribuição ao parâmetro, ou seja, você assegura que na última linha do método, o objeto referênciado pelo identificador definido no parâmetro é o mesmo do início do método.
Finalmente, volto a afirmar, que acredito que o conceito de passagem de parametro por valor ou por referência é algo importante ao paradigma de programação estruturado e que no paradigma orientado a objetos (conceitualmente) isso tem pouca relevância. Na prática, é fundamental saber que o objeto que se está sendo utilizando em um método e que foi passado na lista de parâmetros é o mesmo objeto do código que o chamou.
falou, e feliz ano novo para todos.