Вопрос

Предположим, что в Java есть 3 класса, C расширяется от B, а расширяется от A.

class X {
   interface A {}
   interface B extends A {}
   interface C extends B {}
   void f(A a) {}

   void test() {
      C c = new C()
      B b = (B) c;

      f(b);
   }
}

Если я сделаю что-то подобное, как показано в test () выше:

C c = new C()
B b = (B) c;

f(b);

f () принимает b как тип C , так как C и B оба простираются от A . Я хотел, чтобы f () получал b как тип B , а не как C .

Есть ли какой-либо способ вызвать это отторжение?

Это было полезно?

Решение

f () будет всегда получать что-то, напечатанное как A (несмотря на то, что под покровом это на самом деле B или C и может быть соответственно понижено).

Таким образом, вы можете определить дополнительный f ()

f(B b);

и при необходимости

f(C c);

и будет вызываться правильный в зависимости от класса аргумента. то есть компилятор определяет, какая функция вызывается в зависимости от типа аргумента. Это отличается от динамической отправки (или полиморфизма), которая происходит во время выполнения.

Обратите внимание, что ваш актерский состав в вопросе является излишним. Вы можете написать:

C c = new C()
B b = c;

f(b);

поскольку C простирается от B, C является B.

Другие советы

Вы, похоже, не понимаете разницы между типом времени компиляции и типом времени выполнения.

Вы создаете объект (на который указывают ссылки c и b) типа C, и он останется a C, поскольку невозможно изменить тип среды выполнения объекта и, следовательно, поведение; путем приведения вы можете просто изменить его тип времени компиляции, который влияет на то, как компилятор его обрабатывает.

Можете ли вы дать больше информации о конкретной проблеме, которую вы пытаетесь решить? Скорее всего, есть способ достичь своей цели, изменив дизайн.

  

Я хотел, чтобы f получал b как тип B, а не как тип C.

A C - это B - это A .

Внутри f f видит только часть A своего параметра a , если f вызывает открытую функцию A , которая переопределяется в C , вызывается переопределение C .

Вот как работают виртуальные функции. Идея в том, что упомянутый объект действительно является C , поэтому он должен демонстрировать поведение C . Если вы хотите поведение B , передайте экземпляр B , а не экземпляр C .

Если «C» и «B» должны иметь одинаковое поведение, не проверяйте это поведение в C .

Почему вы хотите это сделать?

Ваш вопрос не имеет смысла. Что вы подразумеваете под "f принимает b как тип C"?

f принимает b как тип A, поскольку сигнатура метода говорит "A". Если вы вызываете методы на b, они будут вызываться на C, если C переопределяет их. Но это стандартное поведение в Java (все методы похожи на виртуальные методы в C ++), и изменить его невозможно.

Возможно, вы сможете описать вашу реальную проблему, тогда мы сможем вам помочь.

Тот факт, что вы можете передать любой подкласс A в качестве параметра f (A a), присущ OO в Java, вы никак не можете обойти это. Если C расширяет A, вы можете всегда использовать его там, где ожидается A.

вы можете использовать отражение, чтобы проверить, относится ли параметр к классу A:

public void f(A a) {
  if (a.getClass() == A.class) {
    // ok, go on
  }
  else {
    System.err.println("The parameter is not of class A!");
  }
}

Не знаю, хотите ли вы этого, но это может быть полезно.

Ваша проблема может быть решена путем понимания различий между типом REFERENCE и типом INSTANCE. Когда вы преобразуете свой объект C в B, вы меняете только ссылочный тип - фактический экземпляр объекта все еще является объектом C - это должно быть совершенно очевидно, если вы посмотрите на свой объект в режиме отладки. Изменение ссылочного типа влияет только на то, как компилятор обрабатывает / воспринимает код - на поведение среды выполнения это не должно повлиять. Любой вызов метода объекта C всегда будет вызывать метод C (независимо от того, был ли объект приведен к чему-либо другому). Если вы переопределили метод из B и хотите вызвать версию метода B, то вам нужно будет создать экземпляр B.

Тот факт, что ваш код имеет ссылку на A, не означает, что объект, на который указывают, тоже является A. Если это C, то он остается C. Ссылка в вашем источнике только ограничивает доступные методы в вашем источнике. Преобразование необходимо только потому, что иногда нужен метод другого типа, и нам нужно обмануть компилятор, чтобы он позволил нам начать использовать методы для преобразования типов. Естественно, приведение может завершиться неудачей во время выполнения, если попытка окажется недействительной.

In upcasting and downcasting the  object first upcast then downcastenter
class A
{
}
class B extends A
{
}
Class M
{
  psvm(String ar[])
{

   A a1= new B();
   B b2=(B)a1;
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top