AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / coding / Perguntas / 79315298
Accepted
Lorenzo Yang
Lorenzo Yang
Asked: 2024-12-29 17:20:00 +0800 CST2024-12-29 17:20:00 +0800 CST 2024-12-29 17:20:00 +0800 CST

Por que “var” no Java 11 ignora a restrição de acesso “protected”?

  • 772

Hoje, enquanto trabalhava em um projeto para um curso de “Design Patterns” da faculdade (Java 11 necessário), descobri um problema com a restrição de acesso do modificador de acesso que pode ser ignorado declarando var. Eu sei como var é usado, é apenas um açúcar sintático que deixa a inferência de tipo para o compilador.

Não consigo descobrir que tipo de alias a var realmente é aqui:

  • é "Child.InnerChild"? Isso não seria uma incompatibilidade de tipo?
  • "InnerParent"? Isso não ignora o restritor de acesso protegido?

Aqui está meu código simplificado:

public abstract class Parent {
    protected abstract static class InnerParent {
        public InnerParent self() {
            return this;
        }
    }
}
public class Child extends Parent {
    public static class InnerChild extends InnerParent {}
}
import anotherpackage.Child;

/**
 * Compiling with Java 11:
 */
public class Main {
    public static void main(String[] args) {
        // As we expected a compilation error: The returned static type does not match the expected type
        // Child.InnerChild innerChild = new Child.InnerChild().self();

        // As we expected a compilation error: Parent.InnerParent is package visible (protected)
        // Parent.InnerParent innerChild = new Child.InnerChild().self();

        // Why does it compile and run correctly here?
        // var is just syntactic sugar for the compiler type, it should be a Parent.InnerParent alias here,
        // why is var allowed to transgress the protected access restriction?
        var innerChild = new Child.InnerChild().self(); // perhce' non da' errore ? var e' un alias di cosa ?
        System.out.println(innerChild);
        System.out.println(innerChild.getClass().getName());
    }
}

Também perguntei ao ChatGPT, mas ele não está respondendo tão bem quanto eu gostaria e não tenho certeza se está correto:

Por que varfunciona

  • Tipo inferido : O tipo inferido para var innerChildé Parent.InnerParent.
  • Regras de acesso : como o tipo é inferido e não escrito explicitamente no código, o compilador não impõe restrições de acesso para a variável declarada.

Encontrei um novo problema: por que não consigo acessar getClass()?

Problema não é possível acessar getClass()

No entanto, é possível compilar dessa maneira.

System.out.println(((Object) innerChild).getClass().getName());
// OUTPUT: com.github.lorenzoyang.anotherpackage.Child$InnerChild
java
  • 2 2 respostas
  • 846 Views

2 respostas

  • Voted
  1. Best Answer
    rzwitserloot
    2024-12-29T20:37:16+08:002024-12-29T20:37:16+08:00

    A resposta é: varpode representar qualquer coisa que o compilador possa raciocinar sobre como um tipo, mesmo os não-denotáveis: Ele permite que você adie a questão. O erro que você acabou tendo existe desde antes da introdução do varque prova que isso não é um problema com var especificamente; é um efeito pretendido (mas um tanto estranho) que corresponde à especificação de linguagem Java.

    Vamos entrar em detalhes e decompô-los. Mas primeiro:

    Lembrete – GPT é inútil

    GPT é uma ferramenta que produz uma resposta autoritativa. Como em, ele vai continuar tentando responder sua pergunta até que algo apareça que pareça autoritativo. Pode ser um monte de besteira completa e neste caso, de fato é. Geralmente pedir ao GPT por respostas objetivas é uma ideia muito ruim: Claro, para perguntas simples parece magicamente incrível, mas então - era uma pergunta simples. Existem muitas maneiras de obter a resposta para uma pergunta simples. Não é para isso que você deve otimizar - seriam respostas para perguntas complexas. E o GPT é muito ruim nisso. No sentido de que você não consegue dizer. Ele lhe dará uma resposta que soa muito bem.

    Lembre-se disto: Perguntar a um GPT sobre questões esotéricas de linguagem é ridículo. Nunca faça isso.

    Não vou abordar mais as respostas do GPT. Elas são inúteis (possivelmente erradas, possivelmente não erradas. Qualquer dica que elas dão pode ser relevante, ou não. Portanto, inúteis).

    Tipos não denotáveis

    É uma questão de linguagem/compilador, então nos referimos à fonte, o JLS:

    JLS21 §14.4.1 :

    Se o LocalVariableType for var, então deixe T ser o tipo da expressão inicializadora quando tratada como se não aparecesse em um contexto de atribuição e fosse, portanto, uma expressão autônoma (§15.2). O tipo da variável local é a projeção ascendente de T com relação a todas as variáveis ​​de tipo sintético mencionadas por T (§4.10.5).

    E observe também este esclarecimento na caixa de exemplo:

    Observe que algumas variáveis ​​declaradas com var não podem ser declaradas com um tipo explícito, porque o tipo da variável não é denotável.

    A última parte está implícita na definição real (pois a definição não declara em nenhum momento que o tipo que o compilador infere varprecisa ser denotado, portanto você deve inferir que ele não precisa ser; a nota na caixa de exemplo destaca isso).

    E é essencial para entender o que está acontecendo aqui. Aqui está um exemplo de uso de tipo não denotável que é mais fácil de seguir do que o que esta questão encontrou:

    class Example {
      public static void main(String[] args) {
        Object o = new Object() {
          void test() {
            System.out.println("HELLO!");
          }
        };
        o.test(); // compiler error.
    
        var p = new Object() {
          void test() {
            System.out.println("HELLO!");
          }
        };
    
        p.test(); // works!!
      }
    
      void asAReminder() {
        new Object() {
          void test() {
            System.out.println("This has always worked"); 
          }
        }.test();
      }
    }
    

    Cole o acima em um arquivo, compile-o - erro na linha que diz 'compiler error'. Marque-o, compile novamente, execute-o - funciona bem.

    O que é bizarro no sentido de que não há nada que você possa substituir varque faça isso funcionar . O que está acontecendo é que o ptipo de é inferido como o tipo da classe local anônima e esse tipo tem um test()método . Isso sempre fez parte do Java (bem, desde a versão 1.2 ou algo assim, décadas atrás neste ponto), só que varagora permite que você declare variáveis ​​locais com esse tipo. O asAReminder()método mostra isso; que o código 'funciona' (compila e roda como você esperaria) em qualquer versão Java das últimas 2 décadas.

    Portanto, varpode denotar InnerParentmuito bem aqui. Não é 'denotável' - se você literalmente substituir varem seu exemplo por InnerParentele falharia na compilação porque InnerParentnão é acessível, mas varmeramente representa o tipo da expressão da maneira como o compilador a trata. E o compilador tem que suportá-lo . Afinal, isso é perfeitamente legal:

    Object o = new Child.InnerChild().self();
    

    O compilador terá que lidar com o fato de que a expressão atribuída oaqui é InnerParent ( self()afinal, a definição de diz isso) e, portanto, terá que descobrir no contexto que isso é aceitável.

    Nós também podemos provar isso!

    Prova de que isso não tem nada a ver comvar

    Tente substituir sua maindefinição pela seguinte:

    public class Main {
        public static void main(String[] args) {
            new Child.InnerChild().self().getClass();
        }
    }
    

    Esse código não será compilado, com exatamente o mesmo erro:

    Object.getClass() is defined in an inaccessible class or interface
    

    Em outras palavras, no Java 8 (que não suporta var nada - varcomo um recurso foi adicionado no Java 10) podemos ter o mesmo conceito: Uma expressão que é, ela própria, legal, e cujo tipo não é acessível no contexto em que a escrevemos. Ela 'funciona', mas o tipo é extremamente venenoso: Se você 'tocar' nela, quase tudo que você fizer com ela será um erro do compilador; mesmo invocando finalmétodos herdados diretamente do obviamente acessível java.lang.Object. Praticamente a única coisa que você pode fazer com eles é tratá-los como um tipo acessível primeiro (por meio de cast ou atribuição a uma variável) ou passá-los para outro método na íntegra.

    varapenas permite que você adie a solução.

    • 20
  2. Sweeper
    2024-12-29T20:19:38+08:002024-12-29T20:19:38+08:00

    var innerChild = new Child.InnerChild().self();simplesmente não viola nada do que a Especificação da Linguagem Java diz.

    Na Seção 6.6 , é dito que o controle de acesso é aplicado quando você usa um nome qualificado .

    Nomes qualificados são um meio de acesso a membros de pacotes, classes, interfaces, parâmetros de tipo e tipos de referência. Quando o nome de tal membro é classificado de seu contexto (§6.5.1) como um nome de tipo qualificado (denotando um membro de um pacote, classe, interface ou parâmetro de tipo) ou um nome de expressão qualificado (denotando um membro de um tipo de referência), o controle de acesso é aplicado.

    Você não usou o nome InnerParentaqui, então as regras de controle de acesso não são aplicadas.

    Alguns tipos de expressões também impõem controle de acesso. Por exemplo, uma chamada de método em uma expressão primária precisa garantir que o método que está sendo chamado seja acessível. É por isso que a chamada innerChild.getClass()produz um erro. Especificamente, esta cláusula proíbe o acesso:

    Um membro (classe, interface, campo ou método) de uma classe, interface, parâmetro de tipo ou tipo de referência, ou um construtor de uma classe, é acessível somente se (i) a classe, interface, parâmetro de tipo ou tipo de referência for acessível, e (ii) o membro ou construtor for declarado para permitir acesso: [...]

    InnerParentnão é acessível, então seu método getClasstambém não é acessível.

    Veja as letras pequenas antes do cabeçalho da seção 6.6.1 para mais informações.


    varnão é um açúcar sintático simples que é simplesmente substituído/reescrito com o nome do tipo da expressão no RHS. A expressão no RHS pode nem ter um nome (por exemplo, classes anônimas) ou pode não ter um tipo (por exemplo, expressões lambda e referências de método). O tipo de uma varvariável é especificado em 14.4.1

    Se o LocalVariableType for var, então deixe T ser o tipo da expressão inicializadora quando tratada como se não aparecesse em um contexto de atribuição e fosse, portanto, uma expressão autônoma (§15.2). O tipo da variável local é a projeção ascendente de T com relação a todas as variáveis ​​de tipo sintético mencionadas por T (§4.10.5).

    Ao escrever var, você realmente não está usando o nome de InnerParent.

    • 4

relate perguntas

  • Lock Condition.notify está lançando java.lang.IllegalMonitorStateException

  • Resposta de microsserviço Muitos para Um não aparece no carteiro

  • Validação personalizada do SpringBoot Bean

  • Os soquetes Java são FIFO?

  • Por que não é possível / desencorajado definir um lado do servidor de tempo limite de solicitação?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Vue 3: Erro na criação "Identificador esperado, mas encontrado 'import'" [duplicado]

    • 1 respostas
  • Marko Smith

    Por que esse código Java simples e pequeno roda 30x mais rápido em todas as JVMs Graal, mas não em nenhuma JVM Oracle?

    • 1 respostas
  • Marko Smith

    Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores?

    • 1 respostas
  • Marko Smith

    Como faço para corrigir um erro MODULE_NOT_FOUND para um módulo que não importei manualmente?

    • 6 respostas
  • Marko Smith

    `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso?

    • 3 respostas
  • Marko Smith

    Quando devo usar um std::inplace_vector em vez de um std::vector?

    • 3 respostas
  • Marko Smith

    Um programa vazio que não faz nada em C++ precisa de um heap de 204 KB, mas não em C

    • 1 respostas
  • Marko Smith

    PowerBI atualmente quebrado com BigQuery: problema de driver Simba com atualização do Windows

    • 2 respostas
  • Marko Smith

    AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos

    • 1 respostas
  • Marko Smith

    Estou tentando fazer o jogo pacman usando apenas o módulo Turtle Random e Math

    • 1 respostas
  • Martin Hope
    Aleksandr Dubinsky Por que a correspondência de padrões com o switch no InetAddress falha com 'não cobre todos os valores de entrada possíveis'? 2024-12-23 06:56:21 +0800 CST
  • Martin Hope
    Phillip Borge Por que esse código Java simples e pequeno roda 30x mais rápido em todas as JVMs Graal, mas não em nenhuma JVM Oracle? 2024-12-12 20:46:46 +0800 CST
  • Martin Hope
    Oodini Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores? 2024-12-12 06:27:11 +0800 CST
  • Martin Hope
    sleeptightAnsiC `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso? 2024-11-09 07:18:53 +0800 CST
  • Martin Hope
    The Mad Gamer Quando devo usar um std::inplace_vector em vez de um std::vector? 2024-10-29 23:01:00 +0800 CST
  • Martin Hope
    Chad Feller O ponto e vírgula agora é opcional em condicionais bash com [[ .. ]] na versão 5.2? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench Por que um traço duplo (--) faz com que esta cláusula MariaDB seja avaliada como verdadeira? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng Por que `dict(id=1, **{'id': 2})` às vezes gera `KeyError: 'id'` em vez de um TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos 2024-03-20 03:12:31 +0800 CST
  • Martin Hope
    MarkB Por que o GCC gera código que executa condicionalmente uma implementação SIMD? 2024-02-17 06:17:14 +0800 CST

Hot tag

python javascript c++ c# java typescript sql reactjs html

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve