Jaka jest różnica między instanceof A Class.czy istnieje możliwość(...)?

Która z poniższych opcji jest lepsza?

a instanceof B

Lub

B.class.isAssignableFrom(a.getClass())

Jedyną różnicą, o której wiem, Jest to, że gdy 'a' jest równe null, pierwsza zwraca false, a druga rzuca wyjątek. Poza tym, czy zawsze dają ten sam wynik?

Author: Megamug, 2009-01-30

13 answers

Podczas używania instanceof, musisz znać klasę B podczas kompilacji. Podczas używania isAssignableFrom() może być dynamiczny i zmieniać się w czasie wykonywania.

 443
Author: Marc Novakowski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-02 20:04:33

instanceof może być używany tylko z typami referencyjnymi, a nie prymitywnymi. isAssignableFrom() może być używany z dowolnymi obiektami klasy:

a instanceof int  // syntax error
3 instanceof Foo  // syntax error
int.class.isAssignableFrom(int.class)  // true

Zobacz http://java.sun.com/javase/6/docs/api/java/lang/Class.html#isAssignableFrom(java.lang.Class).

 198
Author: Adam Rosenfield,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-30 19:53:16

Mówiąc w kategoriach wydajności:

TL; DR

Użyj isInstance lub instanceof, które mają podobną wydajność. isAssignableFrom jest nieco wolniejszy.

Sortowane według wydajności:

  1. isInstance
  2. instanceof (+ 0.5%)
  3. isassignable from (+ 2.7%)

W oparciu o benchmark 2000 iteracji na Javie 8 Windows x64, z 20 warmup iteracji.

W teorii

Używając softu byt Code viewer możemy przetłumaczyć każdy operator na bytecode.

W kontekście:

package foo;

public class Benchmark
{
  public static final Object a = new A();
  public static final Object b = new B();

  ...

}

JAVA:

b instanceof A;

Bytecode:

getstatic foo/Benchmark.b:java.lang.Object
instanceof foo/A

JAVA:

A.class.isInstance(b);

Bytecode:

ldc Lfoo/A; (org.objectweb.asm.Type)
getstatic foo/Benchmark.b:java.lang.Object
invokevirtual java/lang/Class isInstance((Ljava/lang/Object;)Z);

JAVA:

A.class.isAssignableFrom(b.getClass());

Bytecode:

ldc Lfoo/A; (org.objectweb.asm.Type)
getstatic foo/Benchmark.b:java.lang.Object
invokevirtual java/lang/Object getClass(()Ljava/lang/Class;);
invokevirtual java/lang/Class isAssignableFrom((Ljava/lang/Class;)Z);

Mierząc ile instrukcji bajtowych jest używanych przez każdy operator, możemy spodziewać się instanceof i isInstance aby być szybszym niż isAssignableFrom . Jednak rzeczywista wydajność nie jest określana przez kod bajtowy, ale przez kod maszynowy (który jest zależny od platformy). Zróbmy mikro benchmark dla każdego z operatorów.

Benchmark

Kredyt: jak radzi @aleksandr-dubinsky, i podziękowania dla @yura za dostarczenie kodu podstawowego, oto JMH benchmark (zobacz ten tuning guide):

class A {}
class B extends A {}

public class Benchmark {

    public static final Object a = new A();
    public static final Object b = new B();

    @Benchmark
    @BenchmarkMode(Mode.Throughput)
    @OutputTimeUnit(TimeUnit.MICROSECONDS)
    public boolean testInstanceOf()
    {
        return b instanceof A;
    }

    @Benchmark
    @BenchmarkMode(Mode.Throughput)
    @OutputTimeUnit(TimeUnit.MICROSECONDS)
    public boolean testIsInstance()
    {
        return A.class.isInstance(b);
    }

    @Benchmark
    @BenchmarkMode(Mode.Throughput)
    @OutputTimeUnit(TimeUnit.MICROSECONDS)
    public boolean testIsAssignableFrom()
    {
        return A.class.isAssignableFrom(b.getClass());
    }

    public static void main(String[] args) throws RunnerException {
        Options opt = new OptionsBuilder()
                .include(TestPerf2.class.getSimpleName())
                .warmupIterations(20)
                .measurementIterations(2000)
                .forks(1)
                .build();

        new Runner(opt).run();
    }
}

Dał wyniki (wynik to liczba operacji w jednostce czasu, więc im wyższy wynik, tym lepiej):

Benchmark                       Mode   Cnt    Score   Error   Units
Benchmark.testIsInstance        thrpt  2000  373,061 ± 0,115  ops/us
Benchmark.testInstanceOf        thrpt  2000  371,047 ± 0,131  ops/us
Benchmark.testIsAssignableFrom  thrpt  2000  363,648 ± 0,289  ops/us

Warning

  • benchmark jest zależny od JVM i platformy. Ponieważ nie ma znaczących różnic między każdą operacją, może być możliwe uzyskanie innego wyniku (a może innej kolejności!) na innej wersji Javy i / lub platformach takich jak Solaris, Mac czy Linux.
  • benchmark porównuje wydajność " jest B instancja a", gdy" B rozszerza a " bezpośrednio. Jeśli hierarchia klas jest głębsza i bardziej złożona (jak B rozszerza X, które rozszerza Y, które rozszerza Z, które rozszerza A), wyniki mogą być inne.
  • zazwyczaj zaleca się napisanie kodu najpierw wybierając jeden z operatorów (najwygodniejszy), a następnie profilowanie kodu, aby sprawdzić, czy nie ma wąskiego gardła wydajności. Może ten operator jest znikomy w kontekście Twojego kodu, a może...
  • w stosunku do poprzedniego punktu, instanceof W kontekście kodu może być łatwiej zoptymalizowany niż isInstance na przykład...

Aby podać przykład, weź następującą pętlę:

class A{}
class B extends A{}

A b = new B();

boolean execute(){
  return A.class.isAssignableFrom(b.getClass());
  // return A.class.isInstance(b);
  // return b instanceof A;
}

// Warmup the code
for (int i = 0; i < 100; ++i)
  execute();

// Time it
int count = 100000;
final long start = System.nanoTime();
for(int i=0; i<count; i++){
   execute();
}
final long elapsed = System.nanoTime() - start;

Dzięki JIT kod jest w pewnym momencie zoptymalizowany i otrzymujemy:

  • instanceof: 6ms
  • isInstance: 12ms
  • isAssignableFrom: 15ms

Uwaga

Pierwotnie ten post robił swój własny benchmark używając Dla pętli w surowej Javie, co dało niewiarygodne wyniki, ponieważ pewna optymalizacja, taka jak Just In Time, może wyeliminować pętlę. Tak więc chodziło głównie o pomiar czasu, jaki kompilator JIT zajmował, aby zoptymalizować pętlę: zobacz test wydajności niezależny od liczby iteracji Po Więcej Szczegółów

Podobne pytania

 98
Author: JBE,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-27 15:42:09

Bardziej bezpośrednim odpowiednikiem a instanceof B jest

B.class.isInstance(a)

To działa (zwraca false), gdy a jest null.

 33
Author: user102008,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-15 20:58:53

Oprócz podstawowych różnic wymienionych powyżej, istnieje podstawowa subtelna różnica między operatorem instanceof a metodą isassignablef z klasy.

Przeczytaj instanceof jako " czy ta (lewa część) jest instancją tej lub dowolnej podklasy tej (prawa część) "i przeczytaj x.getClass().isAssignableFrom(Y.class) jako"Czy mogę napisać X x = new Y()". Innymi słowy, Operator instanceof sprawdza czy lewy obiekt jest taki sam czy podklasa prawej klasy, natomiast isAssignableFrom sprawdza czy możemy przypisać obiekt parametru class (from) Do referencji klasa, na której wywołana jest metoda.
Zauważ, że obie z nich uważają rzeczywistą instancję, a nie Typ odniesienia.

Rozważmy przykład 3 klas A, B I C, gdzie C rozszerza B i B rozszerza A.

B b = new C();

System.out.println(b instanceof A); //is b (which is actually class C object) instance of A, yes. This will return true.  
System.out.println(b instanceof B); // is b (which is actually class C object) instance of B, yes. This will return true.  
System.out.println(b instanceof C); // is b (which is actually class C object) instance of C, yes. This will return true. If the first statement would be B b = new B(), this would have been false.
System.out.println(b.getClass().isAssignableFrom(A.class));//Can I write C c = new A(), no. So this is false.
System.out.println(b.getClass().isAssignableFrom(B.class)); //Can I write C c = new B(), no. So this is false.
System.out.println(b.getClass().isAssignableFrom(C.class)); //Can I write C c = new C(), Yes. So this is true.
 23
Author: Ashish Arya,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-07 16:04:36

Jest też inna różnica:

Null instanceof X is false no matter what X is

Null.getClass().isAssignableFrom (X) wyrzuci NullPointerException

 14
Author: S. Ali Tokmen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-01 17:00:01

Jest jeszcze jedna różnica. Jeśli Typ (Klasa) do przetestowania jest dynamiczny, np. przekazany jako parametr metody, instanceof nie wytnie go za Ciebie.

boolean test(Class clazz) {
   return (this instanceof clazz); // clazz cannot be resolved to a type.
}

Ale możesz zrobić:

boolean test(Class clazz) {
   return (clazz.isAssignableFrom(this.getClass())); // okidoki
}

UPS, widzę, że ta odpowiedź jest już omówiona. Może ten przykład jest dla kogoś pomocny.

 9
Author: tkalmijn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-25 16:16:10

Ten wątek dostarczył mi pewnego wglądu w to, jak instanceof różni się od isAssignableFrom, więc pomyślałem, że podzielę się czymś własnym.

Odkryłem, że użycie isAssignableFrom jest jedynym (prawdopodobnie nie jedynym, ale prawdopodobnie najłatwiejszym) sposobem, aby zapytać siebie, czy odniesienie do jednej klasy może przyjmować instancje innej, jeśli nie ma instancji żadnej z klas do wykonania porównania.

Dlatego nie znalazłem użycia operatora instanceof do porównywania przypisalności jako dobrego pomysłu, gdy wszystko, co miałem klasy, chyba że rozważałem stworzenie instancji z jednej z klas; myślałem, że to będzie niechlujne.

 7
Author: Owen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-08 18:47:26

Rozważ następującą sytuację. Załóżmy, że chcesz sprawdzić, czy typ A jest super klasą typu obj, możesz wybrać

... A.class.isassignable (obj.getClass()) ...

LUB

... obj instanceof A ...

Ale rozwiązanie isAssignableFrom wymaga, aby Typ obj był widoczny tutaj. Jeśli tak nie jest (np. typ obj może należeć do prywatnej klasy wewnętrznej), opcja ta jest wyłączona. Jednak rozwiązanie instanceof zawsze będzie działać.

 4
Author: algebra,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-18 20:14:26

Instanceof nie może być używany z typami prymitywnymi lub typami generycznymi. Jak w następującym kodzie:

//Define Class< T > type ... 

Object e = new Object();

if(e instanceof T) {
  // Do something.
}

Błąd jest następujący: Nie można wykonać instanceof check z parametrem typu T. użyj zamiast tego obiektu erasure, ponieważ dalsze ogólne informacje o typie zostaną usunięte w czasie wykonywania.

Nie kompiluje się ze względu na typ erasure usuwający odniesienie do środowiska uruchomieniowego. Jednak poniższy kod będzie kompilowany:

if( type.isAssignableFrom(e.getClass())){
  // Do something.
}
 3
Author: James Drinkard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-05 20:49:45
isAssignableFrom(A, B) =

if (A == B) return true
else if (B == java.lang.Object) return false
else return isAssignableFrom(A, getSuperClass(B))

Powyższy pseudo kod jest definicją, jeśli odniesienia typu / klasy A można przypisać z odniesień typu / klasy B. Jest to definicja rekurencyjna. Dla niektórych może to być pomocne, dla innych może być mylące. Dodaję go, gdyby ktoś uznał go za przydatny. To tylko próba uchwycenia mojego zrozumienia, nie jest to oficjalna definicja. Jest on używany w pewnej implementacji Java VM i działa dla wielu przykładowych programów, więc chociaż nie mogę zagwarantować, że przechwytuje wszystkie aspekty isassignable od, to nie jest całkowicie wyłączone.

 0
Author: Stephan Korsholm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-12 20:00:25

Mówiąc w kategoriach wydajności " 2 " (z JMH):

class A{}
class B extends A{}

public class InstanceOfTest {

public static final Object a = new A();
public static final Object b = new B();

@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public boolean testInstanceOf()
{
    return b instanceof A;
}

@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public boolean testIsInstance()
{
    return A.class.isInstance(b);
}

@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public boolean testIsAssignableFrom()
{
    return A.class.isAssignableFrom(b.getClass());
}

public static void main(String[] args) throws RunnerException {
    Options opt = new OptionsBuilder()
            .include(InstanceOfTest.class.getSimpleName())
            .warmupIterations(5)
            .measurementIterations(5)
            .forks(1)
            .build();

    new Runner(opt).run();
}
}

Daje:

Benchmark                            Mode  Cnt  Score   Error  Units
InstanceOfTest.testInstanceOf        avgt    5  1,972 ? 0,002  ns/op
InstanceOfTest.testIsAssignableFrom  avgt    5  1,991 ? 0,004  ns/op
InstanceOfTest.testIsInstance        avgt    5  1,972 ? 0,003  ns/op

Tak, że możemy wnioskować: instanceof tak szybko jak isInstance () i isassignablef () nie daleko (+0.9% czasu wykonania). Więc nie ma żadnej różnicy cokolwiek wybierzesz

 0
Author: Yura,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-20 17:05:04

Niektóre testy, które przeprowadziliśmy w naszym zespole pokazują, że A.class.isAssignableFrom(B.getClass()) działa szybciej niż B instanceof A. może to być bardzo przydatne, jeśli trzeba to sprawdzić na dużej liczbie elementów.

 -2
Author: Milan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-15 10:44:13