Sobrecarga de Operadores

[quote=Luca]Olá

Durante muitos anos estive convencido de que uma das melhores coisas do Java era não ter sobrecarga de operadores que foi uma das coisas que mais sofri quando aprendi C++ há uns 15 anos atrás.

Mas depois que andei estudando erlang, Scala e até mesmo Ruby, que são linguagen que tem coisas bem mais esquisitas do que isto, penso que Java poderia ter isto sim.

Como disse o CV, não é coisa que todo mundo iria usar a toda hora. Mas nos poucos momentos em que pode ser útil seria muito vantajoso. Um exemplo clássico são as aplicações que precisam de números complexos. Outro momento em que faz falta é quando a gente usa BigDecimal ou quer fazer operações aritméticas com vetores.[/quote]
Não acho ruim a funcionalidade em si, e sim o que pode ser gerado com issso. Poderia facilitar N códigos que trabalho e de muitas outras pessoas.

Ter em algum lugar é diferente de disponibilizar para uso indiscriminado. O seu colega, leitor fanático do JavaRanch, vai ver que tem isso na linguagem e vai sair colocando essa joça em todos os lugares possíveis e imagináveis e transformar um simples “shift-right” em um disparador de processos e consultas intermináveis que só pararia depois de um ack/0 (sim, dividir por 0, claro que ele mudou a “/” também).

[quote=Luca]Quem quiser brincar pode experimentar o JFront (A preprocessor to add C++ style operator overloading to Java.):
http://www.gginc.biz/jfront/index.html

[]s
Luca[/quote]
Depois vou dar uma olhada, conhecer esse tipo de ferramenta faz bem a saúde.

O exemplo que eu coloquei é real, só mude a linguagem de Java para C++ e mude o JavaRanch pelo cprogramming.com . E o pior que ele tinha evangelizado o estagiário a usar isso. :frowning:

Até!

Eu achei que simplificaste demais a acepção do uso da Sobrecarga de Operadores. O que mais critico nela ( como na tupla também, apesar de ser assunto para outro tópico ) é o péssimo uso e a falta de senso na aplicação da mesma. E o pior que isso não afeta somente as linguagens de programação

Como na língua portuguesa, poderíamos ter a disposição vários artifícios como esses, o problema seria fazê-los compreensíveis a todos e que a implementação dos mesmos não ficasse tão vulgar quanto o uso do define do C.
[/quote]

Pode dar algum exemplo ou argumento concreto do porque eu fui simplista?

Eu acho que você perdeu exatamente o ponto principal do meu post e de todos os recursos citados: meta-programação. Sem os mecanismos que você critica (não apenas esses) não existe meta-programação, se uma linguagem não é redefinida usando a si mesma seu modelo de MOP é fraco. Se é para programar ‘normalmelte’, quando os construtos da linguagem te bastam, concordo com você mas quando se quer ir além, como quando usamos Model Driven Design e DSLs, não.

E qual a diferença entre ele fazer isso sobrescrevendo o operador e fazer com um método. Isso é um problema do programador não da linguagem.

Não digo permitir voce sobreescrever em classes bases pois isso sim seria uma zona, mais nas classes que voce criar seria bom.

Muitos livros de C++ recomendam sobrecarga de operadores apenas em classes que representam tipos matemáticos.
Veja por exemplo como seria bom sobrecarga em classes como BigNumber, BidDecimal, Vector (vetor matemático), Angle, etc.

Nas demais classes, o uso é um tanto questionável. Já vi abominações em C++.

O fato é que, infelizmente, não se pode contar que todos os programadores de uma equipe tenham bom senso e nem que todos sejam experientes. Então, via de regra, gosto da idéia do Java de restringir coisas que podem gerar um código pouquíssimo legível.

Não acho ruim a funcionalidade em si, e sim o que pode ser gerado com issso. Poderia facilitar N códigos que trabalho e de muitas outras pessoas.[/quote]

Entao pronto, ueh. Se vc acha que Java nao dah corda suficiente pra imbecis se enforcarem, ou vc trabalha em algum lugar excepcional ou nao tem la tana experiencia. Adicionar sobrecarga de operadores, heranca multipla, closures ou qualquer outra coisa que seja “enabling” nao eh lah uma diferenca tao grande, a nao ser pela mudanca de mentalidade da linguagem em geral.

Leia http://www.martinfowler.com/bliki/SoftwareDevelopmentAttitude.html :wink:

Exato! Isso já fez muito falta (no uso de BigDecimal).
No meu caso, o sistema permite que usuários - super acostumados com excel - definam algumas formulas que o sistema precisa calcular.
Imagine o cara (que não é de TI) escrevendo taxaX.add(valorY). Não é natural.
Nesse caso fui salvo pelo groovy, mas se não fosse permitido usa-lo eu teria problemas.

Fora o uso matematico, acho que como o ViniGodoy disse, o uso é um tanto questionável.

[]´s

O que eu estou achando estranho nessa conversa é achar que sobrecarga de operador só existe para operações aritméticas. Não é. Eu não sei quanto a vocês, mas eu aprendi C++ depois que eu aprendi Java, quase no mesmo período do Ruby on Rails. E eu pude perceber o seguinte: em C++ tem sobrecarga de operador pra tudo!

Por exemplo: o operador() que faz com que um objeto aja como uma função, o operador-> que “derreferencia” um objeto, mesmo que este esteja alocado no stack, e o operador qualquer_coisa(), que transforma um objeto em outro, sendo possível criar um código desse jeito abaixo, sem nenhum problema de compilação:

Banana banana;
Melancia melancia;
melancia = banana;

O C++ é um caso extremo, é uma linguagem que tem a vantagem de permitir você fazer tudo, e que tem a desvantagem de permitir você fazer tudo.
Porém a sobrecarga é útil associado a outros recursos da linguagem. Em C++, poderíamos usar templates em um método cujo único requisito é que tipo de entrada tivesse o operador +; em Ruby, com a tipagem dinâmica, receberiamos qualquer coisa de um método, também cujo requisito fosse o operador +.
Além disso, em C++, a sobrecarga é o único meio de se adicionar recursos depois que a classe já tivesse sido “fechada”. Já em Ruby, não existe classe fechada.

Em Java, não tem nada parecido com tipagem tipagem dinânica, nada parecido com templates (tá, tem gente que acha que Generics é Template, eu digo que não, pois Generics é muito restritivo e não passa de “syntactic-sugar”), nada para extender um objeto. E ainda por cima, criou-se uma divisão artificial entre tipos primitivos e objetos, que não existe paralelo nem em C++, nem em Ruby.

Acredito que uma linguagem de programação como Java pode sim ter sobrecarga de operadores, desde que fose adicionado outros recursos como alguma refatoração na especificação do Generics (coisas básicas como permitir T var = new T() dentro uma classe que receba um generic), algum recurso de programação orientada a aspectos e a uniformização entre tipos primitivos e objetos.

Na boa verdade aritmética com datas não existe. (Qual seria o resultado de 1/1/2000 + 1/1/2007 ?)
Existe aritmética de intervalos de tempo, e mesmo assim não existe 1 aritmética existem várias.
Por exemplo: 1/1/2007 - 1/1/2000 poderia ser 7 anos , poderia ser 365*7 anos , poderia ser o numero exato de dias transcorridos entre as datas (!) poderia ser o numero de dias uteis entre as duas datas, poderia ser o numero de meses entre as datas, poderia ser o numero de seculos entre as duas datas, etc…

Data é conceito de depende fortemente do conceito de calendário. E calendário é uma subdivisão do tempo de forma que depende de fatores culturais.

Os operadores + e x não estão definidos e o operador - que ainda faz algum sentido, tem N formas de ser intrepretado. E qualquer uma delas resulta em um numero e não em uma data.

Trabalhar com datas é muito mais difícil do que parece. E parece-me que não seria tão boa ideia ter suporte a estas aritméticas usando operadores.

[quote=rodrigo_gomes][quote=Luca]
Como disse o CV, não é coisa que todo mundo iria usar a toda hora. Mas nos poucos momentos em que pode ser útil seria muito vantajoso. Um exemplo clássico são as aplicações que precisam de números complexos. Outro momento em que faz falta é quando a gente usa BigDecimal ou quer fazer operações aritméticas com vetores.
[/quote]

Exato! Isso já fez muito falta (no uso de BigDecimal).
[/quote]

Estou vendo muitos de vc defenderem este ponto.
Mas lembrem-se do porquê de BigDecimal não ser um tipo primitivo e do porque de vc poder usar MathContext e do porquê da divisão ter um modo de arredondamento. Sendo assim o que seria a / b ?
Os operadores poderiam favorecer os casos triviais ( que o groovy resolve) mas não os casos especiais, e ai , teria que entrar o uso dos métodos comuns. Ora, se vai usar os métodos comuns, por consistência e simplicidade use esses metodos sempre e não complice a vida tendo que inferir o que significa a/b

O mesmo poderia ser pensado para tipos como Money. Parece razoável que a + b seja possivel com dinheiro, mas na realidade não é. Se a e b não tem a mesma unidade, não são somáveis. Mas eu poderia querer , mesmo assim ,somar. Usando o padrão MoneyBag. Como definir qual eu quero usar ? Com métodos é simples :
a.plus(b) e a.add(b) , com operadores teria que inventar dois operadores diferentes. a +_ b e a _+ b ? :shock:

Se existir a possibilidade de construir esses operadores, concerteza alguem os vai definir. Mas alguem entende o que eles significam só de olhar ?

A utilidade dos operadores , quaisquer que sejam , é diretamente proporcional à unicidade do seu significado.

a + b para numeros é simples, mas a/ b nem sempre é simples. Para inteiros tem certas regras, paras doubles tem outras, mas ambas são bem definidias ( ou seja, não existem casos desconhecidos)
alguem deu o exemplo do + para substituir append() no StringBuffer/Builder, so que + já é um operador entre CharSequence

StringBuffer a
StringBuffer b

c = a + b , significa c = new StringBuilder().append( String.valueOf(a)).append( String.valueOf(b) );
e não c = a.append(b). A diferença está no new; c tem que ser um objeto tal que c != a e c != b
caso contrario vira uma zona. a.append(b) não pode retornar um buffer diferente, ele retrorna this exactamente
para não forçar a criação de objetos intermediários ( que é o objetivo de usar buffer/builder)

Usar + é prático, mas todo o mundo sabe que não se deve usar. StringBuffer (StringBuilder) é preferivel.
Isso é suficiente para mostrar como o uso de sobrecarga de operadores é danoso e perigoso, mesmo até aquele que o java já tem.

Operador entre CharSequence ? onde ?

pelo que eu saiba voce naum pode fazer isso:

CharSequence b1 = new StringBuilder();
CharSequence b2 = new StringBuilder();

System.out.println( b1 + b2 );

Não… no caso significaria:

StringBuilder a = new StringBuilder("A e");
StringBuilder b = new StringBuilder(" B");
StringBuilder c;

c = a + b

onde o operador + sobrecarregado seria:

    public StringBuilder +(CharSequence s) {
        return this.append( s );
    }

sem o new.

Operador entre CharSequence ? onde ?
[/quote]

Bom, na verdade é entre qq Object. eu restringi a CharSequence para ter mais significado no contexto
dos operadores, mas ok.

Porquê ? que erro é que dá ?

Acho que o maior problema aqui é o seguinte.

Programadores são seres humanos.

E seres humanos pensam de formas diferentes, e tem conceitos diferentes do que é "natural" e do que é "bom senso".
Os implementadores do cout e cin tiveram bom senso ao fazer isso?

int x;
cin &gt&gt x;
cout &lt&lt "Vinícius tem " &lt&lt x &lt&lt " anos";

Além do mais, não se pode esperar que todos numa equipe tenham experiência e nem bom-senso. Um dos pontos fortes do Java é que, com pouco esfoço, você pode entender o que um programa está fazendo. Os métodos tem nomes explícitos. A própria sun estimulou a clareza do código ao fornecer uma api com pouquíssimas abreviações. Nada de strcat e sim String.concat. A comunidade como um todo seguiu essa filosofia.

Então, me questiono. Até que ponto perder a vantagem da sobrecarga é realmente um benefício? Perder a clareza não pode trazer mais mal do que bem? Vi algumas pessoas falando que outras linguagens são mais rebuscadas e que portanto o java tem “espaço” para isso, mas isso é realmente uma vantagem?

Muita gente pode alegar: "Mas é só ler a documentação e entender que o fulano já se acostuma". Ok, mas ler documentação e entender é um custo chamado "curva de aprendizado". E para interiorizar um conceito, ele terá que ler uma documentação não uma, mas duas, três ou quatro vezes. E alguém terá que manter essa documentação atualizada. Alguém aí já trabalhou num projeto com uma documentação 100% atualizada? Se você já teve problemas para entender um método sem uma boa documentação, imagine um operador sem uma boa documentação!

[quote=ViniGodoy]Acho que o maior problema aqui é o seguinte.

Programadores são seres humanos.

E seres humanos pensam de formas diferentes, e tem conceitos diferentes do que é "natural" e do que é "bom senso".
Os implementadores do cout e cin tiveram bom senso ao fazer isso?

int x;
cin &gt&gt x;
cout &lt&lt "Vinícius tem " &lt&lt x &lt&lt " anos";

Além do mais, não se pode esperar que todos numa equipe tenham experiência e nem bom-senso. Um dos pontos fortes do Java é que, com pouco esfoço, você pode entender o que um programa está fazendo. Os métodos tem nomes explícitos. A própria sun estimulou a clareza do código ao fornecer uma api com pouquíssimas abreviações. Nada de strcat e sim String.concat. A comunidade como um todo seguiu essa filosofia.

Então, me questiono. Até que ponto perder a vantagem da sobrecarga é realmente um benefício? Perder a clareza não pode trazer mais mal do que bem? Vi algumas pessoas falando que outras linguagens são mais rebuscadas e que portanto o java tem “espaço” para isso, mas isso é realmente uma vantagem?

Muita gente pode alegar: "Mas é só ler a documentação e entender que o fulano já se acostuma". Ok, mas ler documentação e entender é um custo chamado "curva de aprendizado". E para interiorizar um conceito, ele terá que ler uma documentação não uma, mas duas, três ou quatro vezes. E alguém terá que manter essa documentação atualizada. Alguém aí já trabalhou num projeto com uma documentação 100% atualizada? Se você já teve problemas para entender um método sem uma boa documentação, imagine um operador sem uma boa documentação![/quote]

Onde eu assino ?

Concordo com a sobrecarga!!!
Não acho que o código fica mais confuso acho q por default não deveria haver e dar erro de compilação… e somente ter sobrecarga classes onde fosse obvio seu uso, números, string, etc.

	PolynomialFunction a = new PolynomialFunction();
	PolynomialFunction b = new PolynomialFunction();
	PolynomialFunction sum = null;
	PolynomialFunction mult = null;
	
	...
	
            // sem sobrecarga
	sum = PolynomialFunction.sum(a, b);
	mult = PolynomialFunction.mult(a, b);
            // com sobrecarga
	sum = a + b;
	mult = a * b;
	
	...