Por que o Java não oferece sobrecarga de operador?

406

Vindo de C ++ para Java, a pergunta sem resposta óbvia é por que o Java não incluiu sobrecarga de operador?

Não é Complex a, b, c; a = b + c;muito mais simples do que Complex a, b, c; a = b.add(c);?

Existe uma razão conhecida para isso, argumentos válidos para não permitir sobrecarga do operador? A razão é arbitrária ou perdida no tempo?

rengolin
fonte
11
@zzzz, tenho dificuldade em ler esse artigo. Isso foi traduzido automaticamente ou o inglês é o segundo idioma do escritor? Acho a discussão aqui muito mais limpa.
25
Para as pessoas que consideram isso não construtivo, essa pergunta rendeu alguns dos diálogos mais construtivos que já vi no SO. Talvez seja um candidato melhor para programmers.stackexchange.com , mas há momentos em que acho que o SO está sendo excessivamente desprezador de assuntos mais amplos.
@NoNaMe é fácil, basta inserir mentalmente um e os artlcles que faltam são uma revelação absoluta de que a pessoa não é um falante nativo de inglês ou um programador (ou como esse cara, ambos :) A razão pela qual os programadores podem deixar artigos é que eles podem torne os comentários mais curtos e se encaixem mais facilmente no espaço fornecido. A partir daí, eles se acostumam. Meu problema é com o layout, de alguma forma eu estou sempre acessando esse site nas pesquisas do Google. Felizmente, existe uma grande extensão do Chrome chamada Claramente, que reformata maravilhosamente as páginas de difícil leitura.
ycomp
11
Não vejo nenhuma razão pela qual e como o OP aceitou a primeira resposta? A resposta escrita por @ stackoverflow.com/users/14089/paercebal é excelente. Deve ser aceito.
destructor

Respostas:

13

Supondo que você desejasse substituir o valor anterior do objeto referido por a, uma função de membro teria que ser chamada.

Complex a, b, c;
// ...
a = b.add(c);

No C ++, essa expressão diz ao compilador para criar três (3) objetos na pilha, executar adição e copiar o valor resultante do objeto temporário no objeto existente a.

No entanto, em Java, operator=não executa cópia de valor para tipos de referência, e os usuários podem criar apenas novos tipos de referência, não tipos de valor. Portanto, para um tipo definido pelo usuário chamado Complex, atribuição significa copiar uma referência para um valor existente.

Considere em vez disso:

b.set(1, 0); // initialize to real number '1'
a = b; 
b.set(2, 0);
assert( !a.equals(b) ); // this assertion will fail

Em C ++, isso copia o valor, portanto a comparação resultará diferente. Em Java, operator=executa a cópia de referência, portanto, ae bagora está se referindo ao mesmo valor. Como resultado, a comparação produzirá 'igual', pois o objeto será comparado igual a si mesmo.

A diferença entre cópias e referências apenas aumenta a confusão de sobrecarga do operador. Como o @Sebastian mencionou, Java e C # precisam lidar com igualdade de valor e referência separadamente - operator+provavelmente lidariam com valores e objetos, mas operator=já estão implementados para lidar com referências.

No C ++, você deve lidar apenas com um tipo de comparação por vez, para que seja menos confuso. Por exemplo, em Complex, operator=e operator==são ambos trabalhando em valores - copiando valores e comparando os valores respectivamente.

Aaron
fonte
6
É bem simples, realmente ... Simplesmente goste de Python e não tenha tarefas sobrecarregadas.
L̳o̳̳n̳̳g̳̳p̳o̳̳k̳̳e̳̳
225
Esta resposta não responde à pergunta. Você está simplesmente insultando o uso do sinal de igual por java. Se b + C retornasse um novo complexo, então a = b + c seria perfeitamente válido e, sim, muito mais simples de ler. Mesmo se você quiser modificar um local, a.set (b + c) é muito mais fácil de ler - especialmente quando a aritmética é mais do que trivial: a.set ((a b + b c) / 5) ou a = a.multiplicar (b) .adicionar (b.multiplicar (c)). dividir (5). Sua escolha ..
BT
24
Ou eu acho que .. não a sua escolha, como o caso
BT
9
No C ++, os Modelos de Expressão resolvem o problema da cópia extra. Praticamente todas as principais bibliotecas aritméticas usam essa técnica por esse mesmo motivo. Além disso, isso não aborda a questão, já que a = b + c é apenas açúcar sintático para a.foo (b.bar (c)), que é realmente a observação inicial na questão.
Kaz Dragon
18
Esta não é a resposta para a pergunta. Esta é a especulação de alguém sobre certas diferenças entre Java e C ++.
SChepurin
804

Existem muitos posts reclamando sobre a sobrecarga do operador.

Eu senti que tinha que esclarecer os conceitos de "sobrecarga do operador", oferecendo um ponto de vista alternativo sobre esse conceito.

Código ofuscante?

Este argumento é uma falácia.

A ofuscação é possível em todos os idiomas ...

É tão fácil ofuscar código em C ou Java por meio de funções / métodos quanto em C ++ por sobrecargas do operador:

// C++
T operator + (const T & a, const T & b) // add ?
{
   T c ;
   c.value = a.value - b.value ; // subtract !!!
   return c ;
}

// Java
static T add (T a, T b) // add ?
{
   T c = new T() ;
   c.value = a.value - b.value ; // subtract !!!
   return c ;
}

/* C */
T add (T a, T b) /* add ? */
{
   T c ;
   c.value = a.value - b.value ; /* subtract !!! */
   return c ;
}

... Mesmo nas interfaces padrão do Java

Para outro exemplo, vamos ver a Cloneableinterface em Java:

Você deveria clonar o objeto que implementa essa interface. Mas você pode mentir. E crie um objeto diferente. De fato, essa interface é tão fraca que você pode retornar outro tipo de objeto, apenas por diversão:

class MySincereHandShake implements Cloneable
{
    public Object clone()
    {
       return new MyVengefulKickInYourHead() ;
    }
}

Como a Cloneableinterface pode ser abusada / ofuscada, ela deve ser banida pelos mesmos motivos que a sobrecarga do operador C ++ deve ser?

Poderíamos sobrecarregar o toString()método de uma MyComplexNumberclasse para que ele retornasse a hora estrita do dia. A toString()sobrecarga também deve ser proibida? Poderíamos sabotar MyComplexNumber.equalspara que ele retornasse um valor aleatório, modificasse os operandos ... etc. etc. etc etc.

Em Java, como em C ++, ou qualquer outra linguagem, o programador deve respeitar um mínimo de semântica ao escrever código. Isso significa implementar uma addfunção que adiciona e um Cloneablemétodo de implementação que clona e um ++operador que incrementa.

O que é ofuscante, afinal?

Agora que sabemos que o código pode ser sabotado mesmo por meio dos métodos Java, podemos nos perguntar sobre o uso real da sobrecarga de operadores em C ++?

Notação clara e natural: métodos versus sobrecarga do operador?

A seguir, compararemos, para casos diferentes, o "mesmo" código em Java e C ++, para ter uma idéia de que tipo de estilo de codificação é mais claro.

Comparações naturais:

// C++ comparison for built-ins and user-defined types
bool    isEqual          = A == B ;
bool    isNotEqual       = A != B ;
bool    isLesser         = A <  B ;
bool    isLesserOrEqual  = A <= B ;

// Java comparison for user-defined types
boolean isEqual          = A.equals(B) ;
boolean isNotEqual       = ! A.equals(B) ;
boolean isLesser         = A.comparesTo(B) < 0 ;
boolean isLesserOrEqual  = A.comparesTo(B) <= 0 ;

Observe que A e B podem ser de qualquer tipo em C ++, desde que as sobrecargas do operador sejam fornecidas. Em Java, quando A e B não são primitivos, o código pode se tornar muito confuso, mesmo para objetos primitivos (BigInteger, etc.) ...

Acessores e subscrição de matriz / contêiner naturais:

// C++ container accessors, more natural
value        = myArray[25] ;         // subscript operator
value        = myVector[25] ;        // subscript operator
value        = myString[25] ;        // subscript operator
value        = myMap["25"] ;         // subscript operator
myArray[25]  = value ;               // subscript operator
myVector[25] = value ;               // subscript operator
myString[25] = value ;               // subscript operator
myMap["25"]  = value ;               // subscript operator

// Java container accessors, each one has its special notation
value        = myArray[25] ;         // subscript operator
value        = myVector.get(25) ;    // method get
value        = myString.charAt(25) ; // method charAt
value        = myMap.get("25") ;     // method get
myArray[25]  = value ;               // subscript operator
myVector.set(25, value) ;            // method set
myMap.put("25", value) ;             // method put

Em Java, vemos que para cada contêiner fazer a mesma coisa (acessar seu conteúdo por meio de um índice ou identificador), temos uma maneira diferente de fazê-lo, o que é confuso.

No C ++, cada contêiner usa a mesma maneira de acessar seu conteúdo, graças à sobrecarga do operador.

Manipulação de tipos avançados naturais

Os exemplos abaixo usam um Matrixobjeto, encontrado usando os primeiros links encontrados no Google para " objeto Java Matrix " e " objeto C ++ Matrix ":

// C++ YMatrix matrix implementation on CodeProject
// http://www.codeproject.com/KB/architecture/ymatrix.aspx
// A, B, C, D, E, F are Matrix objects;
E =  A * (B / 2) ;
E += (A - B) * (C + D) ;
F =  E ;                  // deep copy of the matrix

// Java JAMA matrix implementation (seriously...)
// http://math.nist.gov/javanumerics/jama/doc/
// A, B, C, D, E, F are Matrix objects;
E = A.times(B.times(0.5)) ;
E.plusEquals(A.minus(B).times(C.plus(D))) ;
F = E.copy() ;            // deep copy of the matrix

E isso não se limita a matrizes. As classes BigIntegere BigDecimalde Java sofrem da mesma verbosidade confusa, enquanto seus equivalentes em C ++ são tão claros quanto os tipos internos.

Iteradores naturais:

// C++ Random Access iterators
++it ;                  // move to the next item
--it ;                  // move to the previous item
it += 5 ;               // move to the next 5th item (random access)
value = *it ;           // gets the value of the current item
*it = 3.1415 ;          // sets the value 3.1415 to the current item
(*it).foo() ;           // call method foo() of the current item

// Java ListIterator<E> "bi-directional" iterators
value = it.next() ;     // move to the next item & return the value
value = it.previous() ; // move to the previous item & return the value
it.set(3.1415) ;        // sets the value 3.1415 to the current item

Funções naturais:

// C++ Functors
myFunctorObject("Hello World", 42) ;

// Java Functors ???
myFunctorObject.execute("Hello World", 42) ;

Concatenação de texto:

// C++ stream handling (with the << operator)
                    stringStream   << "Hello " << 25 << " World" ;
                    fileStream     << "Hello " << 25 << " World" ;
                    outputStream   << "Hello " << 25 << " World" ;
                    networkStream  << "Hello " << 25 << " World" ;
anythingThatOverloadsShiftOperator << "Hello " << 25 << " World" ;

// Java concatenation
myStringBuffer.append("Hello ").append(25).append(" World") ;

Ok, em Java você também pode usar MyString = "Hello " + 25 + " World" ;... Mas, espere um segundo: isso está sobrecarregando o operador, não é? Não é trapaça ???

:-D

Código genérico?

Os mesmos operandos modificadores de código genérico devem ser utilizáveis ​​tanto para embutidos / primitivos (que não têm interfaces em Java), objetos padrão (que não podem ter a interface correta) e objetos definidos pelo usuário.

Por exemplo, calculando o valor médio de dois valores de tipos arbitrários:

// C++ primitive/advanced types
template<typename T>
T getAverage(const T & p_lhs, const T & p_rhs)
{
   return (p_lhs + p_rhs) / 2 ;
}

int     intValue     = getAverage(25, 42) ;
double  doubleValue  = getAverage(25.25, 42.42) ;
complex complexValue = getAverage(cA, cB) ; // cA, cB are complex
Matrix  matrixValue  = getAverage(mA, mB) ; // mA, mB are Matrix

// Java primitive/advanced types
// It won't really work in Java, even with generics. Sorry.

Discutindo sobrecarga do operador

Agora que vimos comparações justas entre código C ++ usando sobrecarga de operador e o mesmo código em Java, agora podemos discutir "sobrecarga de operador" como um conceito.

A sobrecarga do operador existia desde antes dos computadores

Mesmo fora da ciência da computação, há sobrecarga de operadores: Por exemplo, em matemática, os operadores gosto +, -, *, etc. estão sobrecarregadas.

Na verdade, o significado de +, -, *, etc. alterações, dependendo dos tipos dos operandos (numerics, vetores, funções de onda quântica, matrizes, etc.).

Muitos de nós, como parte de nossos cursos de ciências, aprendemos várias significados para os operadores, dependendo do tipo de operando. Nós os achamos confusos?

A sobrecarga do operador depende de seus operandos

Essa é a parte mais importante da sobrecarga de operadores: como na matemática ou na física, a operação depende dos tipos de operandos.

Portanto, saiba o tipo do operando e você saberá o efeito da operação.

Mesmo C e Java possuem sobrecarga de operador (codificada)

Em C, o comportamento real de um operador mudará de acordo com seus operandos. Por exemplo, adicionar dois números inteiros é diferente de adicionar dois duplos, ou mesmo um inteiro e um duplo. Existe até todo o domínio aritmético do ponteiro (sem conversão, você pode adicionar a um ponteiro um número inteiro, mas não pode adicionar dois ponteiros ...).

Em Java, não há aritmética de ponteiro, mas alguém ainda achava que a concatenação de strings sem o +operador seria ridícula o suficiente para justificar uma exceção no credo "sobrecarregar o operador é mau".

Só que você, como codificador C (por motivos históricos) ou Java (por motivos pessoais , veja abaixo), não pode fornecer o seu próprio.

Em C ++, a sobrecarga do operador não é opcional ...

No C ++, a sobrecarga do operador para tipos internos não é possível (e isso é uma coisa boa), mas os tipos definidos pelo usuário podem ter sobrecargas de operador definidas pelo usuário .

Como já foi dito anteriormente, em C ++, e ao contrário de Java, os tipos de usuário não são considerados cidadãos de segunda classe da linguagem, quando comparados aos tipos internos. Portanto, se os tipos internos têm operadores, os tipos de usuários também devem poder tê-los.

A verdade é que, como a toString(), clone(), equals()os métodos são para Java ( ou seja, quase-padrão semelhante ), C ++ sobrecarga de operador é tanto parte de C ++ que se torna tão natural como os operadores C originais, ou a antes métodos Java mencionados.

Combinado com a programação de modelos, a sobrecarga do operador se torna um padrão de design bem conhecido. De fato, você não pode ir muito longe no STL sem usar operadores sobrecarregados e sobrecarregar operadores para sua própria classe.

... mas não deve ser abusado

A sobrecarga do operador deve se esforçar para respeitar a semântica do operador. Não subtraia em um +operador (como em "não subtraia em uma addfunção" ou "retorne porcaria em um clonemétodo").

A sobrecarga de elenco pode ser muito perigosa, pois pode levar a ambiguidades. Portanto, eles devem ser realmente reservados para casos bem definidos. Quanto &&e ||, nunca sobrecarregá-los a menos que você realmente saiba o que está fazendo, como você vai perder a avaliação do curto-circuito que os operadores nativos &&e ||desfrutar.

Então ... Ok ... Então por que não é possível em Java?

Porque James Gosling disse isso:

Eu deixei de fora a sobrecarga do operador como uma opção bastante pessoal, porque eu tinha visto muitas pessoas abusarem dela em C ++.

James Gosling. Fonte: http://www.gotw.ca/publications/c_family_interview.htm

Por favor, compare o texto de Gosling acima com o de Stroustrup abaixo:

Muitas decisões de projeto em C ++ têm suas raízes na minha aversão por forçar as pessoas a fazer as coisas de alguma maneira específica [...] Muitas vezes, eu fui tentado a proibir um recurso que eu pessoalmente não gostei, me abstive de fazê-lo porque não achava que tinha. o direito de forçar meus pontos de vista sobre os outros .

Bjarne Stroustrup. Fonte: O Design e Evolução do C ++ (1.3 Contexto Geral)

A sobrecarga do operador beneficiaria o Java?

Alguns objetos se beneficiariam muito da sobrecarga do operador (tipos concretos ou numéricos, como BigDecimal, números complexos, matrizes, contêineres, iteradores, comparadores, analisadores, etc.).

No C ++, você pode aproveitar esse benefício devido à humildade do Stroustrup. Em Java, você está simplesmente ferrado por causa da escolha pessoal de Gosling .

Poderia ser adicionado ao Java?

Os motivos para não adicionar sobrecarga de operador agora em Java podem ser uma mistura de políticas internas, alergia ao recurso, desconfiança dos desenvolvedores (você sabe, os sabotadores que parecem assombrar equipes Java ...), compatibilidade com as JVMs anteriores, hora de escrever uma especificação correta, etc.

Portanto, não prenda a respiração esperando por esse recurso ...

Mas eles fazem isso em c # !!!

Sim...

Embora isso esteja longe de ser a única diferença entre os dois idiomas, este nunca deixa de me divertir.

Aparentemente, os C # pessoas, com sua "cada primitiva é um struct, e um structderiva de objeto" , deu certo na primeira tentativa.

E eles fazem isso em outras línguas !!!

Apesar de todo o FUD contra a sobrecarga de operador definida usada, os seguintes idiomas são compatíveis: Scala , Dart , Python , F # , C # , D , Algol 68 , Smalltalk , Groovy , Perl 6 , C ++, Ruby , Haskell , MATLAB , Eiffel , Lua , Clojure , Fortran 90 , Swift , Ada , Delphi 2005 ...

Tantas línguas, com tantas filosofias diferentes (e às vezes opostas), e ainda assim todas elas concordam com esse ponto.

Alimento para o pensamento...

paercebal
fonte
50
Esta é uma excelente resposta. Não concordo, mas ainda é uma excelente resposta. Eu acho que os problemas possíveis com sobrecargas ruins excedem o valor das boas sobrecargas.
Douglas Leeder 19/10/08
69
@Douglas Leeder: Obrigado! A sobrecarga do operador é como OOP. Na primeira vez que você aprende a fazê-lo, escreve sobrecargas em todos os lugares, como colocaria classes base e herança em todos os lugares (como ironia doce, a API Java). Mas isso passa muito rápido e, em seguida, você aprecia a possibilidade de não abusar dela. Minha experiência de mais de 10 anos em C ++ é que o número de sobrecargas ruins que eu vi no meu código e no código de outros codificadores é tão baixo que acredito poder contar com eles por um lado. E isso é muito menor que o número de erros gerais com excedentes de sprintf, strcat, memset e buffer.
paercebal
11
@Douglas Leeder: Acredito, depois de discutir sobre isso em outra questão do SO, que a diferença entre "amantes" e "odiadores" da sobrecarga do operador provavelmente é causada por uma diferença na abordagem do código: "Aborrecedores" são mais "funções" são o que importa ", significando que eles esperam que uma função faça uma coisa, e apenas uma coisa. Assim, os operadores devem funcionar como projetado pelo idioma. "Amantes" são mais sobre "objetos devem se comportar", o que significa que eles aceitam mais prontamente que a função (e, portanto, os operadores) podem mudar seu comportamento de acordo com o tipo de seus parâmetros.
paercebal
103
Resposta épica. Um dos debunks mais qualificados que eu já li.
Sebastian Mach
7
@MaartenBodewes: Todos os exemplos que escrevi acima, e tudo o que incomoda você é o "como desenvolvedor, você está ferrado porque a escolha pessoal de Gosling" ? Por favor, escreva sua própria resposta, defendendo o ângulo "seus desenvolvedores são estúpidos, deixe que o gênio decida por você o que você precisa" . Esta discussão não serve para nada.
paercebal
44

James Gosling comparou o design do Java ao seguinte:

"Existe esse princípio de mudança, quando você muda de um apartamento para outro. Um experimento interessante é empacotar seu apartamento e colocar tudo em caixas, depois passar para o próximo apartamento e não desfazer as malas até que você precise. Então você ' você está fazendo sua primeira refeição e está tirando algo de uma caixa.Depois de um mês ou mais, você o usa para descobrir de que coisas em sua vida você realmente precisa e depois tira o resto do coisas - esqueça o quanto você gosta ou é legal - e você simplesmente joga fora.É incrível como isso simplifica sua vida, e você pode usar esse princípio em todos os tipos de problemas de design: não faça as coisas apenas porque elas é legal ou só porque é interessante ".

Você pode ler o contexto da citação aqui

Basicamente, a sobrecarga do operador é excelente para uma classe que modela algum tipo de ponto, moeda ou número complexo. Mas depois disso você começa a ficar sem exemplos rapidamente.

Outro fator foi o abuso do recurso em C ++ por desenvolvedores sobrecarregando operadores como '&&', '||', os operadores de elenco e, é claro, 'novos'. A complexidade resultante da combinação disso com a passagem por valor e exceções é bem abordada no livro Excepcional em C ++ .

Garth Gilmour
fonte
6
Você poderia fornecer um exemplo de código da "complexidade da sobrecarga do operador combinada com passagem por valor e exceções"? Apesar de alguns anos brincando com a linguagem, possuindo e tendo lido todos os livros eficazes / excepcionais sobre C ++, não entendo o que você quer dizer com isso.
paercebal
60
O que funciona para James Gosling não vai funcionar para todos. Ele é incrivelmente míope por extrapolar seu experimento de embalagem "interessante" para significar "Jogue fora tudo no mundo que eu não preciso, para que ninguém possa usar essas coisas". Ele claramente não sabe o que eu preciso ou uso.
BT
49
@BT: A maioria enlightning é ponto de vista de Gosling quando comparado ao ponto de vista de Stroustrup sobre esta questão: Many C++ design decisions have their roots in my dislike for forcing people to do things in some particular way [...] Often, I was tempted to outlaw a feature I personally disliked, I refrained from doing so because I did not think I had the right to force my views on others. (B. Stroustrup).
21411 paercebal
29
@ Monkey Software: "C ++, amplamente criticado contra o outro, Java, amplamente apreciado" Este é o hype do marketing. Lembre-se de que o C ++ cresceu sozinho, enquanto o Java (e o .NET) lucraram com as escavadeiras de marketing. Não parece estranho que, para uma "linguagem amplamente apreciada", o Java esteja limitado a aplicativos de servidor, enquanto "amplamente criticado" (provavelmente por desenvolvedores e gerentes de Java que desejam reduzir o custo de produção de código) o C ++ passe de muito alto servidores de desempenho para jogos de alto desempenho? [...]
paercebal
16
@ Hasan: Cada idioma tem seus hacks, os genéricos de Java são um ótimo exemplo disso. Agora, aproximadamente I'd like them to go have a look at some C++ code out there that is hideously put together with weird hacks and "exceptional" features of the language: Programadores ruins escreverão códigos incorretos, independentemente do idioma. Apenas tente emular uma "passagem por referência" para que os parâmetros de função em Java tenham uma idéia. Eu vi o código e ri tanto que doeu. Esse é o tipo de coisa que Gosling não usou, portanto, precisava de hackers horríveis para ter em Java, ainda assim, existe nativamente, a custo zero, tanto em C # quanto em C ++.
22412 paercebal
22

Confira Boost.Units: link text

Ele fornece análise dimensional com zero de sobrecarga por sobrecarga do operador. Quanto mais claro isso pode ficar?

quantity<force>     F = 2.0*newton;
quantity<length>    dx = 2.0*meter;
quantity<energy>    E = F * dx;
std::cout << "Energy = " << E << endl;

realmente produziria "Energia = 4 J", o que está correto.

user15793
fonte
11
"Como exatamente se complica a manutenção e onde diabos isso ofusca o código?"
Mooing Duck
13

Os designers de Java decidiram que a sobrecarga do operador era mais problemática do que valia a pena. Simples assim.

Em uma linguagem em que toda variável de objeto é realmente uma referência, a sobrecarga do operador tem o risco adicional de ser completamente ilógica - para um programador de C ++, pelo menos. Compare a situação com a sobrecarga do operador de C # == e Object.Equalse Object.ReferenceEquals(ou como quer que seja chamado).

Sebastian Redl
fonte
8

O Groovy tem sobrecarga de operador e é executado na JVM. Se você não se importa com o desempenho atingido (que diminui todos os dias). É automático com base nos nomes dos métodos. por exemplo, '+' chama o método 'mais (argumento)'.

Noé
fonte
4
Eu gostaria que todas as linguagens pesadas em sintaxe com sobrecarga de operador usassem essa técnica. Eu nunca entendi por que eles têm que inventar uma versão especial da nomeação e pesquisa de métodos. Stroustrup não menciona nenhuma alternativa no D & EC ++. A equipe de C # adotou a abordagem correta com a sintaxe do Linq ( where ...torna-se .Where(i => ... ). Se eles tivessem feito o mesmo com operadores aritméticos, muitas coisas seriam mais simples e mais poderosas. Java tem a vantagem de uma lista limpa e pode fazer isso direito (embora por razões religiosas, provavelmente nunca será).
Daniel Earwicker
@ DanielEarwicker, eu sempre notei que, quando há divergências complicadas, as pessoas identificam as motivações de ambos os lados como sendo de natureza "religiosa".
@noah, eu poderia viver com um subconjunto limitado de sobrecarga de operadores como esse, desde que houvesse uma tag especial nos nomes dos métodos que os mantivessem visualmente distintos. Algo como definir um método __plus () para a implementação de um OL "+" e ficar longe sobrecarregando coisas como cast e até mesmo subscritos de array. O que não estou disposto a conviver é com o modo como o C ++ e o C # consideraram adequado para implementá-lo.
2
Não é uma resposta. Existem muitos idiomas em execução na VM. A sobrecarga do operador não deve ser uma boa razão para mudar de idioma.
Maarten Bodewes
6

Eu acho que essa pode ter sido uma escolha consciente do design para forçar os desenvolvedores a criar funções cujos nomes claramente comunicam suas intenções. Em C ++, os desenvolvedores sobrecarregavam os operadores com funcionalidades que geralmente não tinham relação com a natureza comumente aceita de um determinado operador, tornando quase impossível determinar o que um pedaço de código faz sem observar a definição do operador.

user14128
fonte
14
In C++ developers would overload operators with functionality that would often have no relation to the commonly accepted nature of the given operator: Esta é uma afirmação gratuita. Sou desenvolvedor profissional de C ++ há 12 anos e raramente encontrei esse problema. Na verdade, a maioria dos bugs e erros de projeto que eu vi em C ++ foram no código de estilo C ( void *, moldes, etc.)
paercebal
6
-1. Toda variável que você atribui é um símbolo, assim como os símbolos do operador aritmético. Se você usa uma frase para nomear essa variável, uma única palavra ou uma única letra, a decisão é sua (ou da sua equipe). Quem deve dizer o que é significativo e o que não é? A resposta é você, o programador. Na matemática pura, multiplicação entre matrizes significa algo diferente da multiplicação entre dois números na aritmética básica. No entanto, usamos os mesmos símbolos para os dois tipos de multiplicação.
Engenheiro
2
@ paercebal: Infelizmente, a afirmação está correta. Você não precisa procurar além do IOstreams para vê-lo em ação. Felizmente, a maioria dos desenvolvedores é mais cautelosa ao inventar novas semânticas para operadores existentes.
Ben Voigt
5
@BenVoigt: [...] E nem estou mencionando o fato de que a addfunção pode realmente ser mal utilizada (como fazer uma multiplicação ou adquirir um mutex) ... O abuso mencionado pelo user14128 não se limita aos operadores, mas existe algum tipo de medo patológico sobre a sobrecarga de operadores que, acredito, vem dos dias anteriores do C vs. C ++, um medo que não foi modificado diretamente para Java, mas, felizmente, não foi usado em C # ... No final, respeitando a semântica e escrever funções / operadores claros é o trabalho do desenvolvedor. Não é o idioma.
22414 paercebal
3
@ jbo5112: Exemplo: cout << f() || g(); os parênteses não o tornam mais claro, eles o corrigem. E se os operadores de troca de bits não estivessem sendo abusados, não seriam necessários. Por que é cout << (5&3) << endl;melhor que cout.fmt(5&3)(endl);? Usar o operador de chamada de função em uma variável de membro functor seria um design infinitamente melhor para fluxos do que redirecionar operadores bit a bit apenas porque o glifo parece bom. Mas isso está longe de ser a única coisa errada nos fluxos.
Ben Voigt
5

Bem, você pode realmente dar um tiro no próprio pé com sobrecarga do operador. É como nos ponteiros as pessoas cometem erros estúpidos com eles e, por isso, foi decidido tirar a tesoura.

Pelo menos eu acho que é esse o motivo. Estou do seu lado de qualquer maneira. :)

Sarien
fonte
Como, por exemplo, este erro estúpido ...
Amir Kirsh 4/17
2
Essa é uma maneira muito ruim de pensar. Você pode dar um tiro no próprio pé, nós preferimos cortar as mãos para baixo, para que você não consiga. E é claro que supomos que você é um idiota que se matará.
Ntj 13/0518
5

Algumas pessoas dizem que a sobrecarga do operador em Java levaria à ofuscação. Essas pessoas já pararam para ver algum código Java fazendo algumas contas básicas, como aumentar um valor financeiro em uma porcentagem usando o BigDecimal? ... a verbosidade de tal exercício se torna sua própria demonstração de ofuscação. Ironicamente, adicionar sobrecarga de operador ao Java nos permitiria criar nossa própria classe Currency, o que tornaria esse código matemático elegante e simples (menos ofuscado).

Volksman
fonte
4

Dizer que a sobrecarga do operador leva a erros lógicos do tipo que o operador não corresponde à lógica da operação, é como não dizer nada. O mesmo tipo de erro ocorrerá se o nome da função for inadequado para a lógica de operação - então qual é a solução: abandone a capacidade de uso da função !? Esta é uma resposta cômica - "Inadequado para a lógica de operação", todo nome de parâmetro, toda classe, função ou qualquer outra coisa que possa ser lógica e inadequada. Eu acho que essa opção deve estar disponível em uma linguagem de programação respeitável, e aqueles que pensam que é inseguro - ei, nenhum deles diz que você precisa usá-la. Vamos pegar o c #. Eles caíram nos ponteiros, mas ei - há uma declaração de 'código inseguro' - programa como você quiser por seu próprio risco.

Kvant
fonte
4

Tecnicamente, há sobrecarga do operador em todas as linguagens de programação que podem lidar com diferentes tipos de números, por exemplo, números inteiros e reais. Explicação: O termo sobrecarga significa que existem simplesmente várias implementações para uma função. Na maioria das linguagens de programação, diferentes implementações são fornecidas para o operador +, uma para números inteiros e outra para reais, isso é chamado de sobrecarga do operador.

Agora, muitas pessoas acham estranho que Java tenha sobrecarga de operador para o operador + para adicionar seqüências de caracteres juntas, e do ponto de vista matemático isso seria realmente estranho, mas visto do ponto de vista de um desenvolvedor de linguagem de programação, não há nada errado em adicionar sobrecarga de operador integrada para o operador + para outras classes, por exemplo, String. No entanto, a maioria das pessoas concorda que depois que você adiciona sobrecarga interna para + para String, geralmente é uma boa ideia fornecer essa funcionalidade também para o desenvolvedor.

Discordo completamente da falácia de que a sobrecarga do operador ofusca o código, pois isso é deixado para o desenvolvedor decidir. É ingênuo pensar e, para ser sincero, está ficando velho.

+1 para adicionar sobrecarga de operador no Java 8.

Olai
fonte
O uso de Java +para concatenar qualquer coisa string-ish é IMHO bastante hediondo, assim como a sobrecarga de /C e FORTRAN para divisão inteira e fracionária. Em muitas versões do Pascal, o uso de operadores aritméticos em qualquer tipo numérico produzirá resultados numericamente equivalentes à conversão dos operandos Real, embora os resultados que podem não ser números inteiros devam ser alimentados através Truncou Roundantes que possam ser atribuídos a números inteiros.
Supercat
2

Supondo que Java como a linguagem de implementação, a, bec sejam referências ao tipo Complex com valores iniciais de null. Supondo também que o Complex seja imutável como o BigInteger mencionado e o BigDecimal imutável semelhante , acho que você quis dizer o seguinte, ao atribuir a referência ao Complex retornada pela adição de bec, e não comparando essa referência a a.

Não é:

Complex a, b, c; a = b + c;

muito mais simples que:

Complex a, b, c; a = b.add(c);
David Schlosnagle
fonte
2
Eu sou? ;) Iguais podem significar atribuição ou comparação, mas = é sempre atribuição e == é sempre comparação. Os nomes podem apresentar grandes fontes de erros.
1

Às vezes, seria bom ter sobrecarga de operador, classes de amigos e herança múltipla.

No entanto, ainda acho que foi uma boa decisão. Se Java tivesse sobrecarregado o operador, nunca poderíamos ter certeza do significado do operador sem examinar o código-fonte. No momento, isso não é necessário. E acho que seu exemplo de uso de métodos em vez de sobrecarga de operador também é bastante legível. Se você quiser deixar as coisas mais claras, pode sempre adicionar um comentário acima das declarações cabeludas.

// a = b + c
Complex a, b, c; a = b.add(c);

fonte
12
Obviamente, como mencionado em outro lugar, você também nunca pode ter certeza do significado da função add.
Eclipse
É verdade que ainda acho reconfortante saber que pelo menos meus operadores são codificados. Obviamente, ter os recursos e usá-los com sensatez nos faria bem. O problema é que é difícil saber se alguém as usou com sensatez. E que você concorda com a definição de sensata. :-)
11
O comentário adicionado para esclarecer o código é como ele seria em um idioma que suportava sobrecarga do operador. Além disso, o fato de o comentário estar escrito em termos de operadores contradiz sua oposição à sobrecarga do operador.
Aluan Haddad
0

Este não é um bom motivo para desaprová-lo, mas é prático:

As pessoas nem sempre o usam com responsabilidade. Veja este exemplo da biblioteca Python scapy:

>>> IP()
<IP |>
>>> IP()/TCP()
<IP frag=0 proto=TCP |<TCP |>>
>>> Ether()/IP()/TCP()
<Ether type=0x800 |<IP frag=0 proto=TCP |<TCP |>>>
>>> IP()/TCP()/"GET / HTTP/1.0\r\n\r\n"
<IP frag=0 proto=TCP |<TCP |<Raw load='GET / HTTP/1.0\r\n\r\n' |>>>
>>> Ether()/IP()/IP()/UDP()
<Ether type=0x800 |<IP frag=0 proto=IP |<IP frag=0 proto=UDP |<UDP |>>>>
>>> IP(proto=55)/TCP()
<IP frag=0 proto=55 |<TCP |>>

Aqui está a explicação:

O operador / foi usado como um operador de composição entre duas camadas. Ao fazer isso, a camada inferior pode ter um ou mais de seus campos padrão sobrecarregados de acordo com a camada superior. (Você ainda pode dar o valor que deseja). Uma string pode ser usada como uma camada bruta.

Sarien
fonte
0

Alternativas ao suporte nativo à sobrecarga do operador Java

Como o Java não possui sobrecarga de operador, aqui estão algumas alternativas que você pode procurar:

  1. Use outro idioma. Tanto o Groovy quanto o Scala têm sobrecarga de operador e são baseados em Java.
  2. Use java-oo , um plug-in que permite a sobrecarga do operador em Java. Observe que NÃO é independente da plataforma. Além disso, possui muitos problemas e não é compatível com as versões mais recentes do Java (por exemplo, Java 10). ( Origem original do StackOverflow )
  3. Use JNI , Java Native Interface ou alternativas. Isso permite que você escreva métodos C ou C ++ (talvez outros?) Para uso em Java. Claro que isso também não é independente de plataforma.

Se alguém tiver conhecimento de outras pessoas, comente e eu adicionarei essa lista.

Gagarwa
fonte
0

Embora a linguagem Java não suporte diretamente a sobrecarga do operador, você pode usar o plug-in do compilador Manifold em qualquer projeto Java para ativá-lo. Ele suporta Java 8 - 13 (a versão atual do Java) e é totalmente suportado no IntelliJ IDEA.

Scott
fonte