Java tira a liberdade do programador?

Pessoal, estava conversando com um amigo (programador C++) sobre linguagens de programação, e ele me veio com um argumento de que “Java tira a liberdade do programador”.

Quais argumentos ele usou para dizer isso?

  • Java não permite ponteiros explícitos, fazendo com que não possamos, por exemplo, mapear um endereço no disco rígido e executar um processamento diretamente sobre ele (mmap) sem bufferizar em memória como se faz em C++.
  • Java verifica se o índice do vetor está dentro do “tamanho” dele, se não tiver dá erro, fazendo a aplicação ficar mais lenta.
  • Java não permite herança múltipla.

Agora, qual a vantagem em se ter “ponteiros explícitos”? (tem classes que nos permitem fazer mmap em java)
Qual a vantagem em se ter herança múltipla?

Concordo que um array fica mais lento por essa questão(de verificar o limite de tamanho), mas tem que ver também que de uma forma ou de outra, o programador teria que fazer uma verificação semelhante em C++. Essa implementação de verificação do tamanho do array, em java, já está otimizada, por exemplo, se usamos em um for, ele não vai verificar em cada elemento do for, mas sim somente onde o otimizador dele disser que existe a possibilidade de acontecer isso.

Sobre a questão da herança multipla, sempre ouço falar do exemplo do CarroAnfíbio, mas ao meu ver, estão invertendo as coisas. Logicamente um carroAnfibio não deve herdar um carro e um barco. Um carro é que tem características de um carroAnfíbio e um barco que tem características de um carroAnfíbio.

Está correto em alguns pontos e errado em outros. Em Java, existem classes que permitem, sim, manipular diretamente a memória (burlando, assim, o mecanismo do GC). No entanto, isso está longe de ser considerado boa prática, já que deixa o programa inseguro (no sentido de permitir que um programa “invada” o espaço de outro).

Quanto ao argumento dos arrays, se encaixa um pouco na questão anterior: por fazer a verificação do ponteiro , a JVM perde um pouco (mas muuuuito pouco, mesmo) de velocidade para checar, mas isso adiciona a segurança de não invadir o espaço de nada na memória e, portanto, não corromper dados utilizados por qualquer programa (incluindo, aí, o próprio programa que invadiu a memória).

Quanto ao exemplo da herança múltipla, essa é uma questão interessante. Existem manuais do próprio C++ que desaconselham o uso de herança múltipla, já que isso pode fazer o compilador “se perder” (o que, inclusive, faz com que se considere o C++ como linguagem “não-assertiva”, ou seja, não dá para garantir o mesmo resultado sempre. Lí sobre isso, inclusive, numa Java Magazine, citada pelo Osvaldo Doederlein. Tentei achar o link para postar aqui mas não achei, desculpe). Ou seja, o melhor a se fazer é, na verdade, garantir a confiabilidade da herança múltipla ou permitir somente herança simples. A equipe do Java optou pela segunda opção. São escolhas :wink:

P.S: Acho que este tópico não deveria ficar na seção “Java Básico”

[]´s

Qualquer linguagem tira a liberdade de quem está utilizando. Porém, a explicação é um pouco mais “filosófica”: é porque não é possível separar a linguagem do pensamento (pensamos através da linguagem), nem é possível separar a linguagem da cultura (a linguagem é o meio de um povo se comunicar). Por isso, aquilo que a linguagem é define aquilo que pensamos e a cultura que a cerca.

Nas linguagens de programação, ocorre a mesma coisa. Cada linguagem define um modo único de pensamento e uma cultura única, limitando o pensamento livre.

De certa maneira, poderia argumentar que C++ também tira a liberdade do programador, porque o prende no conceito de que somente é possível fazer alguma coisa manipulando diretamente referências de memória (ponteiro).

Veja o que Hoare disse sobre os ponteiros explicitos:

[quote]"Their introduction into high-level languages has been a step backward from which we may never recover?
(Sir Tony Hoare 1973, Turing Award 1980)[/quote]

Acredito que eles trazem mais desvantagens do que vantagens.

Ponteiros explícitos fazem diferença em alguns momentos, na famosa função de swap de inteiros por exemplo.

void swap(int *p1, int *p2){ int aux = *p1; *p1 = *p2; *p2 = aux; }

Sinto falta disso em java, você não consegue manipular tipos primitivos na memória, mas isso não é fator preponderante para se abandonar uma linguagem.
Conheço várias pessoas que amam c++ e temos boas “discussões” sobre isso, cada um tem seu ponto de vista.

Na questão da Herança multipla só uma curiosidade, todas as classes por natureza já tem herança de “Object” então uma classe pode ter herança dupla! :B

Quando se fala em herança múltipla seria algo desse tipo (se existisse em java)

class PortaAviao extends Barco, Aeroporto

ou algo assim

Pelo contrário, java da muito mais liberdade para o programador pq faz com que ele possa se preocupar com o que é mais importante, ele não é obrigado a gerenciar memoria, a ficar redimensionando arrays o tempo todo e coisas do tipo.

Sem contar que são poucos os programadores que implementariam em C uma solucao caseira para gerencia de memória melhor que o GC do Java.

Herança múltipla pra que? me diga um caso em que heranca multipla é irrefutavelmente a melhor opção?? Sem contar que na maioria das linguagens em que isso é possivel é comum encontrar “God Classes”.

Não criticando o seu amigo, mas é aquela velha história do cara querer defender a linguagem que programa.

Você deveria conhecer a Standard Library do C++. Esse tipo de problema não existe no c++ a muito tempo.

Programadores C++ são idiotas, tão idiotas quanto os programadores Java. A diferença é que, enquanto os javeiros imaginam seu mega-pattern na sua mega-abstração mega-flexível, os cêzeiros imaginam os cálculos matemáticos malucos pra manipular memória.

Vamos às vantagens citadas pelo seu amigo:

Não é necessário ponteiros explícitos pra mapear arquivos. O Java possui abstrações em alto nível pra isso, dentro do pacote java.nio; é a classe MappedByteBuffer.

Outra, o “endereço” de arquivo (termo correto: índice) não tem nada a ver com endereço do disco rígido. Índices são uma abstração do sistema operacional.

Não dá pra saber se o Java verifica o tamanho do Array a toda hora. Java, e qualquer linguagem de alto nível, gera códigos de execução de maneira que não podemos prever. É bem possível que essas verificações ocorram o mínimo possível.

Java, particularmente, é bastante otimizada. Um código feito em Java não é tão mais lento quanto feito em C. E por ser uma linguagem que te livra de fazer besteiras, o programa resultante pode até ser mais rápido.

De qualquer maneira, velocidade é irrelevante. O que é melhor: uma rotina que executa em 0,001 segundos ou uma que executa em 0,005 segundos? Mudando um pouco a frase: você pagaria, sei lá, três vezes mais por um software para que ele traga resultados em 0,001 segundos ao invés de 0,005?

No mundo de hoje, software é limitado por I/O ou por rede, não por velocidade de CPU. Só pra ter uma ideia, imagine um software hipotético onde você mandaria um XML pela rede: você conseguiria maior velocidade se zipar o XML antes de enviar e dezipar após recebê-lo. O processamento é maior, mas a latência em rede menor.

Sim, mas quando programava em C++, também não a usava.

Só para você ter uma idéia, a JVM pode, em alguns casos, omitir essa verificação de limites do array. Por exemplo, um caso em que ela faz isso é quando você usa um “for” em que os limites estão claramente dentros dos limites do array. Nesse caso, ela pode até efetuar uma compilação para código nativo que nem verifique os limites, já que eles nunca serão alcançados. Ou seja, você tem a segurança do código gerenciado (nome melhor que “interpretado”, porque a JVM não interpreta o código o tempo inteiro, mas sempre tenta compilar para código nativo dentro de determinadas condições) e a velocidade do codigo nativo.

Bom sobre isso achei algo falando sobre no seguinte tópico http://www.guj.com.br/posts/list/74434.java

Flw

Quando se fala em herança múltipla seria algo desse tipo (se existisse em java)

class PortaAviao extends Barco, Aeroporto

ou algo assim[/quote]

Sim sim, sei o q seria a herança multipla, foi apenas uma curiosidade =D

Não, o que voce chama de herança dupla na verdade é hierarquia.
Classe A extende Object
Classe B extende A
Classe C extende B

C extende indiretamente de Object mas por causa da hierarquia, e não herança multipla.

[quote=renamed]Ponteiros explícitos fazem diferença em alguns momentos, na famosa função de swap de inteiros por exemplo.

void swap(int *p1, int *p2){ int aux = *p1; *p1 = *p2; *p2 = aux; }

Sinto falta disso em java, você não consegue manipular tipos primitivos na memória, mas isso não é fator preponderante para se abandonar uma linguagem.
Conheço várias pessoas que amam c++ e temos boas “discussões” sobre isso, cada um tem seu ponto de vista.[/quote]

Nesse caso é besteira mesmo. Mesmo em C++ não é necessário usar ponteiros nesse caso específico.

No C# isso é resolvido usando-se passagem de parâmetros por referência. Não é preciso ponteiros. Um exemplo:

using System;
class TesteReferencia {
    /// <summary>Versão que requer um int</summary>
    public static void Swap (ref int x, ref int y) {
        int tmp = x; x = y; y = tmp;
    }
    /// <summary>Versão genérica (aceita qualquer tipo)</summary>
    public static void Swap<T> (ref T x, ref T y) {
        T tmp = x; x = y; y = tmp;
    }

    public static void Main (String[] args) {
        int v1 = 2, v2 = 3;
        Swap (ref v1, ref v2); 
        Console.WriteLine ("v1 = {0}, v2 = {1}", v1, v2);
        double x = 2.0, y = 3.0;
        Swap (ref x, ref y);
        Console.WriteLine ("x = {0}, y = {1}", x, y);
    }
}

Não, o que voce chama de herança dupla na verdade é hierarquia.
Classe A extende Object
Classe B extende A
Classe C extende B

C extende indiretamente de Object mas por causa da hierarquia, e não herança multipla.[/quote]

Não sabia disso mark então a classe “C” só vai extender Object por que “A” extende? Ta ai, nova essa para mim! =D

Eiffel aceita herança múltipla, mas é necessário especificar de que classe você quer herdar determinado método, se algum problema for detectado durante a compilação. Isso é meio gambiarresco.

Veja:

http://www.eiffel.com/developers/knowledgebase/harnessing_multiple_inheritance.html - vá até a seção “But what about name clashes?”

O .NET simplesmente copiou o design do Java (herança simples). Isso fez com que implementar linguagens com herança múltipla (como a própria Eiffel) ficasse desajeitado.

Mas se eu fizesse um negocio tosco desse tipo

C extende B, B extende A, A extende C!

Isso geraria Exceção de estouro de memória?? (muito provavelmente)

O compilador não deixa isso ocorrer; ele percebe esse tipo de coisa (herança circular), e não permite a compilação.

[quote=entanglement]O compilador não deixa isso ocorrer; ele percebe esse tipo de coisa (herança circular), e não permite a compilação.
[/quote]
Humm, ta ai nunca tinha tentando fazer isso para testar! :B