Strange Behavior When Overriding Private Methods

Strange behavior when overriding private methods

Inheriting/overriding private methods

In PHP, methods (including private ones) in the subclasses are either:

  • Copied; the scope of the original function is maintained.
  • Replaced ("overridden", if you want).

You can see this with this code:

<?php
class A {
//calling B::h, because static:: resolves to B::
function callH() { static::h(); }
private function h() { echo "in A::h"; }
}
class B extends A {
//not necessary; just to make explicit what's happening
function callH() { parent::callH(); }
}
$b = new B;
$b->callH();

Now if you override the private method, its new scope will not be A, it will be B, and the call will fail because A::callH() runs in scope A:

<?php
class A {
//calling B::h, because static:: resolves to B::
function callH() { static::h(); }
private function h() { echo "in A::h"; }
}
class B extends A {
private function h() { echo "in B::h"; }
}
$b = new B;
$b->callH(); //fatal error; call to private method B::h() from context 'A'

Calling methods

Here the rules are as follows:

  • Look in the method table of the actual class of the object (in your case, bar).

    • If this yields a private method:

      • If the scope where the method was defined is the same as the scope of the calling function and is the same as the class of the object, use it.
      • Otherwise, look in the parent classes for a private method with the same scope as the one of the calling function and with the same name.
      • If no method is found that satisfies one of the above requirements, fail.
    • If this yields a public/protected method:

      • If the scope of the method is marked as having changed, we may have overridden a private method with a public/protected method. So in that case, and if, additionally, there's a method with the same name that is private as is defined for the scope of the calling function, use that instead.
      • Otherwise, use the found method.

Conclusion

  1. (Both private) For bar->call(), the scope of call is foo. Calling $this->m() elicits a lookup in the method table of bar for m, yielding a private bar::m(). However, the scope of bar::m() is different from the calling scope, which foo. The method foo:m() is found when traversing up the hierarchy and is used instead.
  2. (Private in foo, public in bar) The scope of call is still foo. The lookup yields a public bar::m(). However, its scope is marked as having changed, so a lookup is made in the function table of the calling scope foo for method m(). This yields a private method foo:m() with the same scope as the calling scope, so it's used instead.
  3. Nothing to see here, error because visibility was lowered.
  4. (Both public) The scope of call is still foo. The lookup yields a public bar::m(). Its scope isn't marked as having changed (they're both public), so bar::m() is used.

Is a private parent method overridden if child has an implementation of it?

The child class simply has no knowledge of any private methods from parent class. Any such method in the scope of child class is undefined. It can't be overridden if it is not defined in the first place.

If the method is public it is visible to everyone and any code can call it. When the method is protected in is only known to the class itself and any of its descendants. When a method is private it is only known in the scope of this class. To any other class this method is simply undefined.

You can define another method with the same name in the child class, but it is not overriding anything. You could still call it overriding, but it would not make much sense. Overriding means that if you do not redefine the method in the child class it will call the definition from parent class. With private methods it is not possible because they are not accessible from the child class.

Consider this example:

class A {
private function toOverridePrivate() {
echo "private toOverridePrivate A\n";
}
}

class B extends A {
public function toOverridePrivate() {
parent::toOverridePrivate();
}
}

$o = new B;

$o->toOverridePrivate();

Fatal error: Uncaught Error: Call to private method A::toOverridePrivate() from context 'B'

Whether the class B defines method toOverridePrivate or not, it makes no difference, because the method toOverridePrivate from A is always inaccessible.

How to override private method from a library class

You can manipulate the behaviour indirectly. This is the snippet you are interested in.

$allCss = $this->css;

if ($this->isStyleBlocksParsingEnabled) {
$allCss .= $this->getCssFromAllStyleNodes($xpath);
}

Looking at the class setters, you can call disableStyleBlocksParsing to prevent the function being called.

The $allCss variable is taken straight from $this->css, which is only modified by the setCss method.

So you have two choices:

  • Extend the class, make isStyleBlocksParsingEnabled false and immutable, then override the setCss method to do what you wanted getCssFromAllStyleNodes to do.
  • Call disableStyleBlocksParsing and and call setCss with preprocessed text.

Here is an example of the first option:

class MyEmogrifier extends Emogrifier
{
public function __construct($html = '', $css = '')
{
parent::__construct($html, $css);

$this->disableStyleBlocksParsing();
}

public function setCss($css)
{
// Preprocess CSS here.

parent::setCss($css);
}
}

So there is no shotgun surgery, or reflection needed.

To be honest though. I would feel much less inclined to even use a library as concrete as this one. I use protected for nearly all of my would be private methods.

Weird Eclipse behaviour when tring to override a method

Other IDE's seeing it as parent method and giving you the correct error message.

Coming to eclipse (that's weird from eclipse side), since there is no @ovveride annotation it is completely treating that as a new method in child, hence no error. If you place @ovveride annotation in child, you see the same error as other IDE's.

It is the same reason that you getting woof woof twice, as it is calling the same method from parent twice as it is assuming you are not calling the new method created from child.

To avoid the confusion it is always recommend that to use the @ovveride annotation while overriding methods.

Overridden methods and variables - Inconsistent behavior

Overriding a method with a weaker access-modifier is prohibited by the standard (§8.4.8.3):

The access modifier (§6.6) of an overriding or hiding method must provide at least as much access as the overridden or hidden method, as follows:

  • If the overridden or hidden method is public, then the overriding or hiding method must be public; otherwise, a compile-time error occurs.

  • If the overridden or hidden method is protected, then the overriding or hiding method must be protected or public; otherwise, a compile-time error occurs.

  • If the overridden or hidden method has default (package) access, then the overriding or hiding method must not be private; otherwise, a compile-time error occurs.

This ensures that any method provided by the base-class can also be called on derived classes within the same context.

Variables can't be overriden. Base.className and Derived.className are two distinct variables. Thus it is perfectly valid to have a variable with the same name and different access-modifier in Derived.

I.e. this code will print false:

class Base{
public String str = "hello";
}

class Derived extends Base{
private String str = "whatever";

public Derived(){
super.str = "abc";
str = "def";
}

void foo(){
System.out.println(str.equals(super.str));
}
}

public static void main(String[] args){
new Derived().foo();
}

The relevant jls-sections:

Field declarations (§8.3):

The scope and shadowing of a field declaration is specified in §6.3 and §6.4.

If the class declares a field with a certain name, then the declaration of that field is said to hide any and all accessible declarations of fields with the same name in superclasses, and superinterfaces of the class.

In this respect, hiding of fields differs from hiding of methods (§8.4.8.3), for there is no distinction drawn between static and non-static fields in field hiding whereas a distinction is drawn between static and non-static methods in method hiding.

A hidden field can be accessed by using a qualified name (§6.5.6.2) if it is static, or by using a field access expression that contains the keyword super (§15.11.2) or a cast to a superclass type.

In this respect, hiding of fields is similar to hiding of methods.

If a field declaration hides the declaration of another field, the two fields need not have the same type.

And Shadowing (§6.4.1):

A declaration d of a field or formal parameter named n shadows, throughout the scope of d, the declarations of any other variables named n that are in scope at the point where d occurs.

static and private method behavior when calling direct on object of child class sounds like overriding?

Polymorphism is not for static methods. Static methods are called with JVM instructions invokestatic, whereas polymorphism is achieved with invokevirtual. The calls to static methods are determined at compile time, and polymorphic methods are dynamically dispatched at runtime.

You can easily tweak your code so that A.staticMethod() is called, by just assigning new B() to a variable of type A.

public static void main(String[] args) {
new B().privateMethod();
A b = new B(); // change here.
b.staticMethod(); // A.staticMethod() is called here.
}

Strange behavior on overriding methods and calling them with shared_ptr

The call

(*entities[0]).resolveCollision(*entities[1]);

resolves to Entity::resolveCollision(Entity &other); at compile time. By virtue of virtual function dispatch mechanism, the call is dispatched to Person::resolveCollision(Entity &other); at run time. However, the dynamic dispatch system does not change the call to Person::resolveCollision(Bullet &other); based on the run time information of other. That would require a double dispatch system, which is not part of the language.

Why is the wrong method called: Strange inheritance behaviour

I'm guessing that you expected m4.E to be printed. But don't forget that overload resolution is performed at compile-time, not at execution time.

Neither C nor B have a method4(E) method available, so the compiler resolves the calls to the method4(A) method... which isn't overridden by E. All the calls you have m4.C // why? in the question are calls to a method with signature method4(A), called on an instance of E. Now E doesn't override method4(A), so it's left with the implementation in C, which prints m4.C.

Nothing strange going on here at all.



Related Topics



Leave a reply



Submit