Passagem por referência em JAVA com STRING

14 respostas
P

Senhores,

Estou acessando um método de uma DLL de um equipamento via JNA, onde este método tem como assinatura int metodo_qualquer( int a, string *b ), onde o parâmetro “b” é um passado como referência, haja visto, que depois da execução do método este terá um valor no qual deverei tratalo. Mas no JAVA, até onde entendo, a passagem de parâmentro funciona por valor, ou seja é passado para o método uma cópia da referência String. Portanto no final da execução do método o valor do parâmentro “b” não muda. Veja exemplo mostrado a seguir:

String b = “teste 1”
System.out.println(“b-” + b); // Imprime “teste 1”
metodo_qualquer(1,b)
System.out.println(“b-” + b); // Imprime “teste 1”

public int metodo_qualquer(a,b) {

String b = teste 2;

return 0;

}

Desta forma, alguem tem alguma dica de como poderei ter o valor da parâmetro “b” alterado mesmo depois da execução do método “metodo_qualquer”. Vale lembrar que a DLL que possui o método “metodo_qualquer” eu não tenho acesso, haja visto, que esta foi desenvolvida em C.

Obrigado.

14 Respostas

M

Cara,

a passagem de parametros em java para objetos é por referencia. Até para String.
O que acontece é que Strings são imutáveis, então toda vez que você vai fazer alguma alteração nela você tem que atribuir novamente o valor na sua váriavel.
Por que você não retorna um char* com o novo valor atualizado? É um problema?

Falou.

E

Leia a documentação.

https://jna.dev.java.net/#char_buffer

P

Mr_Arthur:
Cara,

a passagem de parametros em java para objetos é por referencia. Até para String.
O que acontece é que Strings são imutáveis, então toda vez que você vai fazer alguma alteração nela você tem que atribuir novamente o valor na sua váriavel.
Por que você não retorna um char* com o novo valor atualizado? É um problema?

Falou.

Não tenho controle do método, o método está encapsulado na DLL.

P

Alguém ja usou a classe ByReference do JNA ? Não estou sabendo como usa-la! Pode me ajudar a resolver o problema.

P

Senhores,

Vejam o código postado a seguir, onde antes da execução do método "objModifica.modifica2(
objString.getValue())" o valor da variável "objString " era "Teste - Antes" e depois da execução do método "objModifica.modifica2(objString.getValue())" o conteudo da variável deveria ter sido modificado para "Teste - Depois" e não alterou. Ou seja, o meu objetivo de passar o parâmetro "objString" como referência, para o método objModifica.modifica2() não foi alcançado.

Alguem tem alguma sugestão?

Obrigado a Todos.

- - - - -
Classe Main
- - - - -
package teste;
public class Main{
    public static void main(String[] args) {
        StringByReference objString = new StringByReference("Teste - Antes");
        System.out.println("(Valor Antes do Metodo) ->"+objString.getValue());
        Modifica objModifica = new Modifica();
        objModifica.modifica2(objString.getValue());
        System.out.println("(Valor Depois do Metodo) ->"+objString.getValue());
    }
}

- - - - -
Classe StringByReference
- - - - -
package teste;
import com.sun.jna.ptr.ByReference;
class StringByReference extends ByReference {
    public void setValue(String value) {
        getPointer().setString(0L, value);
    }
    public String getValue() {
        return getPointer().getString(0L);
    }
    public StringByReference(String value) {
        super(value.getBytes().length + 1);
        setValue(value);
    }
}

- - - - -
Classe Modifica
- - - - -
package teste;
public class Modifica{
    public void modifica2(String obj) {
        obj = "Teste - Depois";
    } 
}

- - - - -
Saída do Programa:
- - - - -
(Valor Antes do Metodo) ->Teste - Antes
(Valor Antes do Metodo) ->Teste - Antes
E

Você leu a documentação que lhe foi passada? Basicamente você não pode passar strings por referência, e ponto final. Em vez disso, você deve alocar um array de bytes grande suficiente para o programa em C poder copiar a string (ou seja, o char*) que ele irá retornar, e então você tem de pegar esse array de bytes (que deve conter os caracteres e mais um \0) e passar para o método Native.toString(byte[]). Esse método pega o byte[] que foi alterado pelo método em C, e então acha o terminador da string em C (ou seja, um ‘\0’), muda sua codificação de ANSI para Unicode, e retorna uma String com o valor desejado.
Qual é o tamanho do array a ser passado? Você tem de olhar a documentação do método em C. Por exemplo, se você for chamar uma API do Windows que copia num char* o nome de um arquivo, esse tamanho é MAX_PATH + 1, ou seja, 256. Para outros métodos valem outros valores.

E

Parece tosco, mas é assim mesmo. Acho que só o C# (que tem o tal do “Interop”) tem algo que é bem menos tosco, mas você tem de usar uma série de “attributes” (o equivalente do .NET às nossas “annotations”) para poder dizer exatamente o que deve ser feito - se é o caso de criar um byte[] e ver onde é que termina, ou se é o caso de um BSTR, ou outra coisa mais esquisita ainda.

P

Segue o fragmento de código com o uso de vetor de tipo byte. E também não funcionou. Vale lembrar que meu desejo é que o ao passar o parâmetro "msgCatraca" para o método "WRTReadAccess", depois da execução do método a variável "msgCatraca" deverá conter o conteudo lido pelo equipamento em questão (catraca). Portanto esta variável sempre retorna o conteúdo em BRANCO.

Alguem tem mais alguma sugestão ?????

FileWriter writerCmd = null;
        PrintWriter saidaCmd = null;
        try {
            writerCmd = new FileWriter("log2_catraca.txt");
            saidaCmd = new PrintWriter(writerCmd, true);
            byte[] msgCatraca = new String("").getBytes();
            // Numero do Equipamento
            int numEquipamento = 2;
            // Mensagem a ser retornado pela Catraca
            //  - numEquipamento: Numero do Equipamento
            //  - msgCatraca: Mensagem em BRANCO que será preenchido pelo método "WRTReadAccess" da DLL do equipamento
            int retMsg = demo.WRTReadAccess(numEquipamento, msgCatraca);
            saidaCmd.println("Retorno I             - " + retMsg); // Retorno da mensagem
            saidaCmd.println("Retorno (msgCatraca)  - " + Native.toString(msgCatraca)); // Retornou sempre BRANCO
        } catch (IOException ex) {
            Logger.getLogger(Threads.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            try {
                saidaCmd.close();
                writerCmd.close();
            } catch (IOException ex) {
                Logger.getLogger(Threads.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
E
byte[] msgCatraca = new String("").getBytes();

Isto gera um array que é muito pequeno (tem exatamente 0 bytes).

Quando você fez isso, você passou um array tão pequeno que me espanto que não tenha havido um erro do tipo “programa Java cai sozinho e cria um arquivo hs_err_pid.log em algum diretório”.

Passe um byte array bem maior, e por favor leia o manual da sua API para saber qual é o tamanho adequado para passar para a sua API. Você NUNCA deve passar um array vazio. Eu chutaria que você precisa pasasr pelo menos um array de 1000 bytes.

P

entanglement

Realmente você estava certo, funcionou do modo que você disse, apenas coloquei a mensagem como um vetor do tipo byte de tamanho fixo.
Te devo um salgado.

C

Mas quando eu tiver um casso assim, como eu faço?

String dados = rs.getText("coluna1");
if(dados.equals("Olá")){
     metodo(dados,"Mundo");
}else{
     metodo(null,null);
}

ou

String dados = rs.getText("coluna1");
if(dados.equals("Olá")){
     metodo(dados,"Mundo");
}else{
     metodo("","");
}
S

Mr_Arthur:
Cara,

a passagem de parametros em java para objetos é por referencia. Até para String.
.

Cuidado! Todas as passagens em java são por valor. Todas!

O processo é assim, a variável de origem e a variável de parametros são diferentes. O valor da variável de origem é copiado para variável de parametro.
A passage por referencia acontece quando as variáveis de origem e parametro são a mesma. Isto não existe em java. Todas as variáveis são diferentes e são os valores que são copiados.

É por isto que a interação com JNDI costuma ser por arrays.

V

Sua definição existe, mas não é a única - ela diverge entre os vários autores.

Passar dados por ponteiros, em C++ também é considerado passagem por referência. E funciona exatamente igual ao Java. O método recebe uma cópia do ponteiro, e não do objeto apontado.
Quando falamos em “passar por referência” estamos falando do dado, no caso, o objeto.

Todas as variáveis que apontam para objetos em Java são referências. Portanto, todos os métodos passam o objeto através de referências, não de cópias de seu valor.

De qualquer forma, você estava respondendo a uma pessoa que postou há 3 anos atrás, já que nosso amigo caiofabioa ressuscitou o tópico sem prévio aviso.

V

caiofabioa:
String dados = rs.getText("coluna1"); if(dados.equals("Olá")){ metodo(dados,"Mundo"); }else{ metodo(null,null); }
ou

String dados = rs.getText("coluna1"); if(dados.equals("Olá")){ metodo(dados,"Mundo"); }else{ metodo("",""); }

Isso não tem absolutamente nada a ver com o assunto de passagem de parâmetros.

A abordagem vai depender do que estiver implementado no metodo(String, String).

Mas, de maneira geral, a segunda forma é preferível, já que ela atende ao padrão do objeto nulo:

E, por favor, não ressuscite tópicos à toa. Principalmente, sem deixar claro que está fazendo isso.

Criado 6 de julho de 2010
Ultima resposta 19 de jan. de 2013
Respostas 14
Participantes 6